def stack_options(func): """ stack_options is a decorator which adds the stack and environment arguments to the click function ``func``. :param func: The click function to add the arguments to. :type func: function :returns: function """ func = click.argument("stack")(func) func = click.argument("environment")(func) return func
def change_set_options(func): """ change_set_options is a decorator which adds the environment, stack and change set name arguments to the click function ``func``. :param func: The click function to add the arguments to. :type func: function :returns: function """ func = click.argument("change_set_name")(func) func = click.argument("stack")(func) func = click.argument("environment")(func) return func
def clone_target_arg(): """Represent target to clone/branch.""" return click.argument( "target", metavar="TARGET", type=click.STRING, )
def task_option(__fun: Callable) -> Callable: """Add task selection options. Note: This is only here to reduce duplication in command setup. Args: __fun: Function to add options to Returns: Function with additional options """ __fun = click.option( '-x', '--from-dir', is_flag=True, expose_value=False, is_eager=True, callback=task_from_dir, help='Use directory name as task name.' )(__fun) __fun = click.argument( 'task', default='default', envvar='RDIAL_TASK', required=False, type=TaskNameParamType() )(__fun) return __fun
def decorate_plotting_command(func): """A decorator to make a plotting command.""" func = DatasetQuery.decorate(func) func = CeleryProgressGroup.decorate(func) func = click.argument("component", type=str)(func) func = click.option("--force/--no-force", default=False, help="Force update plots.")(func) return func
def database_connection_arg(fn): arg = click.argument( "database-connection", type=DatabaseConnectionParam(exists=True), doc_help=( "A connection URI for a database, or a path on the file system")) return arg(fn)
def custom_scores_argument(func): def callback(ctx, param, value): min_a = min_arg or 1 mutli = 1 error = '' if ('evaluation' in ctx.meta and ctx.meta['evaluation']) or force_eval: mutli += 1 error += '- %d evaluation file(s) \n' % min_a if 'train' in ctx.meta and ctx.meta['train']: mutli += 1 error += '- %d training file(s) \n' % min_a # add more test here if other inputs are needed min_a *= mutli ctx.meta['min_arg'] = min_a if len(value) < 1 or len(value) % ctx.meta['min_arg'] != 0: raise click.BadParameter( 'The number of provided scores must be > 0 and a multiple of %d ' 'because the following files are required:\n' '- %d development file(s)\n' % (min_a, min_arg or 1) + error, ctx=ctx ) ctx.meta['scores'] = value return value return click.argument( 'scores', type=click.Path(exists=True), callback=callback, **kwargs )(func)
def optional_tools_arg(multiple=False, allow_uris=False): """ Decorate click method as optionally taking in the path to a tool or directory of tools. If no such argument is given the current working directory will be treated as a directory of tools. """ arg_type_class = click.Path if not allow_uris else UriLike arg_type = arg_type_class( exists=True, file_okay=True, dir_okay=True, readable=True, resolve_path=True, ) if allow_uris: name = "uris" if multiple else "uri" else: name = "paths" if multiple else "path" nargs = -1 if multiple else 1 return click.argument( name, metavar="TOOL_PATH", type=arg_type, nargs=nargs, callback=_optional_tools_default, )
def sample_path(fn): arg = click.argument( "sample-path", type=click.Path(exists=True, dir_okay=False, file_okay=True), doc_help=( "The path to the deconvoluted sample file")) return arg(fn)
def connect_options(f): f = click.argument('host', nargs=1)(f) f = click.option('--port', help='port to connect to, default per platform')(f) f = click.option('--type', help='type of connection')(f) f = click.option('--user', help='username', envvar='NGAGE_USER')(f) f = click.option('--password', help='password to use if not using key auth')(f) return f
def post_options(): """Standard arguments and options for posting timeseries readings. """ options = [ click.argument('port'), click.argument('value', type=JSONParamType()), click.option('--timestamp', metavar='DATE', help='the time of the reading'), ] def wrapper(func): func.__doc__ += _post_options_docs for option in reversed(options): func = option(func) return func return wrapper
def db_string_option(f): def callback(ctx, param, value): db = ctx.ensure_object(Database) db.setup(value) return value return click.argument('db_string', expose_value=False, envvar="ROLLUP_DB_STRING", callback=callback)(f)
def decorate(f, **kwargs): """ Work of actually decorating a function -- wrapped in here because we want to dispatch depending on how this is invoked """ metavar = kwargs.get("metavar", "ENDPOINT_ID") f = click.argument("endpoint_id", metavar=metavar, type=click.UUID)(f) return f
def specify_id_attrs(f): try: ids = res.identity_attributes.keys() except AttributeError: ids = res.identity_attributes for id in reversed(ids): f = click.argument(id, required=True)(f) return f
def argument(*args, **kwargs): """Make Click arguments Cloudify specific This exists purely for aesthetic reasons, otherwise Some decorators are called `@click.something` instead of `@cfy.something` """ return click.argument(*args, **kwargs)
def wrap(func): decorators = [ click.option('--parent-type', default=parent_type_default, show_default=True, cls=parent_type_cls, help='type of Girder parent target', type=click.Choice(parent_types)), click.argument('parent_id'), click.argument( 'local_folder', type=click.Path(exists=path_exists, dir_okay=True, writable=path_writable, readable=True), default=path_default, nargs=1 if not multiple_local else -1, required=multiple_local ), ] for decorator in reversed(decorators): func = decorator(func) return func
def domain_argument(f): def callback(ctx, param, value): state = ctx.ensure_object(State) # if no dot in the domain name, assume we should add zone if value.find('.') == -1: value = '%s.%s' % (value, state.ZONE) state.DOMAIN = value return value return click.argument('DOMAIN', expose_value=False, callback=callback)(f)
def required_workflow_arg(): arg_type = click.Path( exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=False, ) return click.argument("workflow_path", metavar="WORKFLOW_PATH", type=arg_type)
def required_tool_arg(): """ Decorate click method as requiring the path to a single tool. """ arg_type = click.Path( exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True, ) return click.argument('path', metavar="TOOL_PATH", type=arg_type)
def optional_tools_or_packages_arg(multiple=False): """ Decorate click method as optionally taking in the path to a tool or directory of tools or a Conda package. If no such argument is given the current working directory will be treated as a directory of tools. """ name = "paths" if multiple else "path" nargs = -1 if multiple else 1 return click.argument( name, metavar="TARGET", nargs=nargs, )
def tool_test_json(): target_path = click.Path( file_okay=True, dir_okay=False, resolve_path=True, ) return click.argument( 'path', metavar="FILE_PATH", type=target_path, default="tool_test_output.json", )
def value_param(f): def validate(ctx, param, value): try: value = yaml.safe_load(value) except (yaml.scanner.ScannerError, yaml.parser.ParserError) as e: msg = 'The value is not valid YAML.\n\n{}\n{}'.format( e.problem, e.problem_mark) raise click.BadParameter(msg) return value decorator = click.argument('value', callback=validate) return decorator(f)
def get_click_client(app: App) -> Callable: @click.group(invoke_without_command=True, help='API Star') @click.option('--version', is_flag=True, help='Display the `apistar` version number.') @click.pass_context def client(ctx: click.Context, version: bool) -> None: if ctx.invoked_subcommand is not None: return if version: from apistar import __version__ click.echo(__version__) else: click.echo(ctx.get_help()) for command in app.commands: command_signature = inspect.signature(command) for param in reversed(list(command_signature.parameters.values())): name = param.name.replace('_', '-') annotation = param.annotation kwargs = {} if hasattr(annotation, 'default'): kwargs['default'] = annotation.default if hasattr(annotation, 'description'): kwargs['help'] = annotation.description if issubclass(annotation, (bool, schema.Boolean)): kwargs['is_flag'] = True kwargs['default'] = False elif hasattr(annotation, 'choices'): kwargs['type'] = click.Choice(annotation.choices) elif hasattr(annotation, 'native_type'): kwargs['type'] = annotation.native_type elif annotation is inspect.Signature.empty: kwargs['type'] = str else: kwargs['type'] = annotation if 'default' in kwargs: name = '--%s' % param.name.replace('_', '-') option = click.option(name, **kwargs) command = option(command) else: kwargs.pop('help', None) argument = click.argument(param.name, **kwargs) command = argument(command) cmd_wrapper = click.command(help=command.__doc__) command = cmd_wrapper(command) client.add_command(command) return client
def signature_analysis(cmd): cmd = click.option('--model', '-m', default=None, type=click.Path(exists=True, dir_okay=False), help='Path to model file.')(cmd) cmd = click.option('--parameter', '-p', default=None, type=click.Path(exists=True, dir_okay=False), help='(optional) Path to parameter file.' )(cmd) cmd = click.argument("scheme", type=click.Path(exists=True, dir_okay=False), required=False )(cmd) return cmd
def auto_create_params(self): argspec = inspect.getargspec(self.real_func) args = argspec.args if args[0] == "self": del args[0] if self.pass_script_info and args[0] == "info": del args[0] num_mandatory_args = len(args) - len(argspec.defaults) if argspec.defaults else len(args) if num_mandatory_args: mandatory_args = args[:num_mandatory_args] mandatory_args.reverse() for arg in mandatory_args: if not self.has_param(arg): click.argument(arg)(self.unbound_func) if len(args) > num_mandatory_args: for i, arg in enumerate(args[num_mandatory_args:]): if not self.has_param(arg): optname = "--%s" % arg.replace("_", "-") default = argspec.defaults[i] if isinstance(default, bool): optname += "/--no-%s" % arg.replace("_", "-") click.option(optname, default=default)(self.unbound_func)
def optional_project_arg(exists=True): arg_type = click.Path( exists=exists, file_okay=False, dir_okay=True, writable=True, resolve_path=True, ) return click.argument( 'path', metavar="PROJECT", default=".", type=arg_type )
def text_arg(__func: Callable) -> Callable: """Add task selection click commands. Note: This is only here to reduce duplication in command setup. Args: __func: Function to add options to Returns: Function with additional options """ return argument('text')(__func)
def _arg_date(f): def callback(ctx, param, value): try: value = dt.strptime(value, ctx.params[date_frmt_key]) except KeyError: raise click.ClickException("Need to use `date_format_opt` when using `date_arg`") except ValueError: raise click.BadParameter( "Cannot parse {v} to date with format {f}".format(v=value, f=ctx.params["date_frmt"]) ) else: return value return click.argument(var, metavar=metavar, callback=callback)(f)
def arg_job_number(f): def callback(ctx, param, value): try: value = int(value) except: raise click.BadParameter("Must specify an integer >= 0") if value < 0: raise click.BadParameter("Must specify an integer >= 0") elif value == 0: return value else: return value - 1 return click.argument("job_number", nargs=1, callback=callback, metavar="<job_number>")(f)
def recipe_arg(multiple=True): name = "paths" if multiple else "path" nargs = -1 if multiple else 1 return click.argument( name, metavar="RECIPE_DIR", type=click.Path( exists=True, file_okay=True, dir_okay=True, resolve_path=True, ), nargs=nargs, callback=_optional_tools_default, )
class DreameVacuum(MiotDevice, VacuumInterface): _mappings = MIOT_MAPPING @command(default_output=format_output( "\n", "Battery level: {result.battery_level}\n" "Brush life level: {result.brush_life_level}\n" "Brush left time: {result.brush_left_time}\n" "Charging state: {result.charging_state}\n" "Cleaning mode: {result.cleaning_mode}\n" "Device fault: {result.device_fault}\n" "Device status: {result.device_status}\n" "Filter left level: {result.filter_left_time}\n" "Filter life level: {result.filter_life_level}\n" "Life brush main: {result.life_brush_main}\n" "Life brush side: {result.life_brush_side}\n" "Life sieve: {result.life_sieve}\n" "Map view: {result.map_view}\n" "Operating mode: {result.operating_mode}\n" "Side cleaning brush left time: {result.brush_left_time2}\n" "Side cleaning brush life level: {result.brush_life_level2}\n" "Time zone: {result.timezone}\n" "Timer enabled: {result.timer_enable}\n" "Timer start time: {result.start_time}\n" "Timer stop time: {result.stop_time}\n" "Voice package: {result.voice_package}\n" "Volume: {result.volume}\n" "Water flow: {result.water_flow}\n" "Water box attached: {result.is_water_box_carriage_attached} \n" "Cleaning time: {result.cleaning_time}\n" "Cleaning area: {result.cleaning_area}\n" "First clean time: {result.first_clean_time}\n" "Total clean time: {result.total_clean_time}\n" "Total clean times: {result.total_clean_times}\n" "Total clean area: {result.total_clean_area}\n", )) def status(self) -> DreameVacuumStatus: """State of the vacuum.""" return DreameVacuumStatus( { prop["did"]: prop["value"] if prop["code"] == 0 else None for prop in self.get_properties_for_mapping(max_properties=10) }, self.model, ) # TODO: check the actual limit for this MANUAL_ROTATION_MAX = 120 MANUAL_ROTATION_MIN = -MANUAL_ROTATION_MAX MANUAL_DISTANCE_MAX = 300 MANUAL_DISTANCE_MIN = -300 @command() def start(self) -> None: """Start cleaning.""" return self.call_action("start_clean") @command() def stop(self) -> None: """Stop cleaning.""" return self.call_action("stop_clean") @command() def home(self) -> None: """Return to home.""" return self.call_action("home") @command() def identify(self) -> None: """Locate the device (i am here).""" return self.call_action("locate") @command() def reset_mainbrush_life(self) -> None: """Reset main brush life.""" return self.call_action("reset_mainbrush_life") @command() def reset_filter_life(self) -> None: """Reset filter life.""" return self.call_action("reset_filter_life") @command() def reset_sidebrush_life(self) -> None: """Reset side brush life.""" return self.call_action("reset_sidebrush_life") @command() def play_sound(self) -> None: """Play sound.""" return self.call_action("play_sound") @command() def fan_speed(self): """Return fan speed.""" dreame_vacuum_status = self.status() fanspeed = dreame_vacuum_status.cleaning_mode if not fanspeed or fanspeed.value == -1: _LOGGER.warning("Unknown fanspeed value received") return return {fanspeed.name: fanspeed.value} @command(click.argument("speed", type=int)) def set_fan_speed(self, speed: int): """Set fan speed. :param int speed: Fan speed to set """ fanspeeds_enum = _get_cleaning_mode_enum_class(self.model) fanspeed = None if not fanspeeds_enum: return try: fanspeed = fanspeeds_enum(speed) except ValueError: _LOGGER.error(f"Unknown fanspeed value passed {speed}") return None click.echo(f"Setting fanspeed to {fanspeed.name}") return self.set_property("cleaning_mode", fanspeed.value) @command() def fan_speed_presets(self) -> FanspeedPresets: """Return available fan speed presets.""" fanspeeds_enum = _get_cleaning_mode_enum_class(self.model) if not fanspeeds_enum: return {} return _enum_as_dict(fanspeeds_enum) @command(click.argument("speed", type=int)) def set_fan_speed_preset(self, speed_preset: int) -> None: """Set fan speed preset speed.""" if speed_preset not in self.fan_speed_presets().values(): raise ValueError( f"Invalid preset speed {speed_preset}, not in: {self.fan_speed_presets().values()}" ) self.set_fan_speed(speed_preset) @command() def waterflow(self): """Get water flow setting.""" dreame_vacuum_status = self.status() waterflow = dreame_vacuum_status.water_flow if not waterflow or waterflow.value == -1: _LOGGER.warning("Unknown waterflow value received") return return {waterflow.name: waterflow.value} @command(click.argument("value", type=int)) def set_waterflow(self, value: int): """Set water flow. :param int value: Water flow value to set """ mapping = self._get_mapping() if "water_flow" not in mapping: return None waterflow = None try: waterflow = WaterFlow(value) except ValueError: _LOGGER.error(f"Unknown waterflow value passed {value}") return None click.echo(f"Setting waterflow to {waterflow.name}") return self.set_property("water_flow", waterflow.value) @command() def waterflow_presets(self) -> Dict[str, int]: """Return dictionary containing supported water flow.""" mapping = self._get_mapping() if "water_flow" not in mapping: return {} return _enum_as_dict(WaterFlow) @command( click.argument("distance", default=30, type=int), ) def forward(self, distance: int) -> None: """Move forward.""" if distance < self.MANUAL_DISTANCE_MIN or distance > self.MANUAL_DISTANCE_MAX: raise DeviceException( "Given distance is invalid, should be [%s, %s], was: %s" % (self.MANUAL_DISTANCE_MIN, self.MANUAL_DISTANCE_MAX, distance)) self.call_action( "move", [ { "piid": 1, "value": "0", }, { "piid": 2, "value": f"{distance}", }, ], ) @command( click.argument("rotatation", default=90, type=int), ) def rotate(self, rotatation: int) -> None: """Rotate vacuum.""" if (rotatation < self.MANUAL_ROTATION_MIN or rotatation > self.MANUAL_ROTATION_MAX): raise DeviceException( "Given rotation is invalid, should be [%s, %s], was %s" % (self.MANUAL_ROTATION_MIN, self.MANUAL_ROTATION_MAX, rotatation)) self.call_action( "move", [ { "piid": 1, "value": f"{rotatation}", }, { "piid": 2, "value": "0", }, ], )
import click from olimage.core.parsers.packages import ParserPackages _arguments = [ click.argument("board"), click.argument("release"), click.argument("variant", type=click.Choice([str(v) for v in ParserPackages().variants])) ] _options = [ click.option("--hostname", help="Set default system hostname"), click.option("--keyboard-keymap", default="gb", help="Set default system keyboard locale"), click.option("--keyboard-layout", default="English (UK)", help="Set default system keyboard locale"), click.option("--locale", default="en_GB.UTF-8", help="Set default system locale"), click.option("--ssh/--no-ssh", default=True, help="Enable/Disable ssh access"), click.option("--timezone", default="Europe/London", help="Set default system timezone") ] def parameters(func): for argument in reversed(_arguments): func = argument(func) for option in reversed(_options): func = option(func) return func
def image_exists(image): # docker image accept tags only in version >= 1.8... # so workaround with docker history rc = subprocess.call("docker history -q {0} > /dev/null".format(image), shell=True) if rc == 0: return True elif rc == 1: return False else: raise RuntimeError("Cannot test if image exists") image_option = click.argument("image", type=click.Choice(["centos7", "jessie"])) salt_option = click.option('--salt', is_flag=True, help="Run salt highstate") @click.group() def cli(): pass @cli.command(help="Build a container") @image_option @salt_option @click.option('--tag', default=None, help="Custom tag name for the built docker image") @click.option('--file-root', type=click.Path(exists=True), default='test/salt')
def package_arg(f): def callback(ctx, param, value): state = ctx.ensure_object(State) state.installstate.packages.extend(value) return value return argument('packages', nargs=-1, callback=callback, expose_value=False,)(f)
import click username_arg = click.argument("username") cloud_alias_option = click.option( "--cloud-alias", help="If the employee has an email alias other than their Code42 username " "that they use for cloud services such as Google Drive, OneDrive, or Box, " "add and monitor the alias. WARNING: Adding a cloud alias will override any " "existing cloud alias for this user.", ) notes_option = click.option("--notes", help="Optional notes about the employee.")
class Alarm(GatewayDevice): """Class representing the Xiaomi Gateway Alarm.""" @command(default_output=format_output("[alarm_status]")) def status(self) -> str: """Return the alarm status from the device.""" # Response: 'on', 'off', 'oning' return self._gateway.send("get_arming").pop() @command(default_output=format_output("Turning alarm on")) def on(self): """Turn alarm on.""" return self._gateway.send("set_arming", ["on"]) @command(default_output=format_output("Turning alarm off")) def off(self): """Turn alarm off.""" return self._gateway.send("set_arming", ["off"]) @command() def arming_time(self) -> int: """Return time in seconds the alarm stays 'oning' before transitioning to 'on'.""" # Response: 5, 15, 30, 60 return self._gateway.send("get_arm_wait_time").pop() @command(click.argument("seconds")) def set_arming_time(self, seconds): """Set time the alarm stays at 'oning' before transitioning to 'on'.""" return self._gateway.send("set_arm_wait_time", [seconds]) @command() def triggering_time(self) -> int: """Return the time in seconds the alarm is going off when triggered.""" # Response: 30, 60, etc. return self._gateway.get_prop("alarm_time_len").pop() @command(click.argument("seconds")) def set_triggering_time(self, seconds): """Set the time in seconds the alarm is going off when triggered.""" return self._gateway.set_prop("alarm_time_len", seconds) @command() def triggering_light(self) -> int: """Return the time the gateway light blinks when the alarm is triggerd.""" # Response: 0=do not blink, 1=always blink, x>1=blink for x seconds return self._gateway.get_prop("en_alarm_light").pop() @command(click.argument("seconds")) def set_triggering_light(self, seconds): """Set the time the gateway light blinks when the alarm is triggerd.""" # values: 0=do not blink, 1=always blink, x>1=blink for x seconds return self._gateway.set_prop("en_alarm_light", seconds) @command() def triggering_volume(self) -> int: """Return the volume level at which alarms go off [0-100].""" return self._gateway.send("get_alarming_volume").pop() @command(click.argument("volume")) def set_triggering_volume(self, volume): """Set the volume level at which alarms go off [0-100].""" return self._gateway.send("set_alarming_volume", [volume]) @command() def last_status_change_time(self) -> datetime: """Return the last time the alarm changed status.""" return datetime.fromtimestamp( self._gateway.send("get_arming_time").pop())
from py42.exceptions import Py42NotFoundError from py42.exceptions import Py42UpdateClosedCaseError from code42cli.bulk import generate_template_cmd_factory from code42cli.bulk import run_bulk_process from code42cli.click_ext.groups import OrderedGroup from code42cli.errors import Code42CLIError from code42cli.file_readers import read_csv_arg from code42cli.options import format_option from code42cli.options import sdk_options from code42cli.options import set_begin_default_dict from code42cli.options import set_end_default_dict from code42cli.output_formats import OutputFormatter case_number_arg = click.argument("case-number", type=int) case_number_option = click.option( "--case-number", type=int, help="The number assigned to the case.", required=True ) name_option = click.option("--name", help="The name of the case.",) assignee_option = click.option( "--assignee", help="The UID of the user to assign to the case." ) description_option = click.option("--description", help="The description of the case.") findings_option = click.option("--findings", help="Any findings for the case.") subject_option = click.option( "--subject", help="The user UID of the subject of the case." ) status_option = click.option( "--status", help="Status of the case. `OPEN` or `CLOSED`.",
def nodata_handler(ctx, param, value): """Get nodata value from a template file or command line.""" retval = from_like_context(ctx, param, value) if retval is None and value is not None: try: retval = float(value) except: raise click.BadParameter("%s is not a number." % repr(value), param=param, param_hint='nodata') return retval # Singular input file file_in_arg = click.argument('INPUT', callback=file_in_handler) # Singular output file file_out_arg = click.argument('OUTPUT', type=click.Path(resolve_path=True)) bidx_opt = click.option('-b', '--bidx', type=int, default=1, help="Input file band index (default: 1).") bidx_mult_opt = click.option('-b', '--bidx', multiple=True, help="Indexes of input file bands.")
import click from simpletasks import Cli, Task from gapi_helper.drive import DriveService, Folder @click.group() def drive(): pass @Cli( drive, params=[ click.argument( "credentials", type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True), ), click.argument("user"), ], ) class ListOwnedFilesTask(Task): def do(self) -> None: DriveService.configure(self.options.get("credentials")) user = self.options.get("user") delegatedService = DriveService(user).getService() page_token = None while True: response = ( delegatedService.files() .list(
class Ceil(Device): """Main class representing Xiaomi Philips LED Ceiling Lamp.""" # TODO: - Auto On/Off Not Supported # - Adjust Scenes with Wall Switch Not Supported _supported_models = SUPPORTED_MODELS @command(default_output=format_output( "", "Power: {result.power}\n" "Brightness: {result.brightness}\n" "Color temperature: {result.color_temperature}\n" "Scene: {result.scene}\n" "Delayed turn off: {result.delay_off_countdown}\n" "Smart night light: {result.smart_night_light}\n" "Automatic color temperature: {result.automatic_color_temperature}\n", )) def status(self) -> CeilStatus: """Retrieve properties.""" properties = ["power", "bright", "cct", "snm", "dv", "bl", "ac"] values = self.get_properties(properties) return CeilStatus(defaultdict(lambda: None, zip(properties, values))) @command(default_output=format_output("Powering on")) def on(self): """Power on.""" return self.send("set_power", ["on"]) @command(default_output=format_output("Powering on")) def off(self): """Power off.""" return self.send("set_power", ["off"]) @command( click.argument("level", type=int), default_output=format_output("Setting brightness to {level}"), ) def set_brightness(self, level: int): """Set brightness level.""" if level < 1 or level > 100: raise CeilException("Invalid brightness: %s" % level) return self.send("set_bright", [level]) @command( click.argument("level", type=int), default_output=format_output("Setting color temperature to {level}"), ) def set_color_temperature(self, level: int): """Set Correlated Color Temperature.""" if level < 1 or level > 100: raise CeilException("Invalid color temperature: %s" % level) return self.send("set_cct", [level]) @command( click.argument("brightness", type=int), click.argument("cct", type=int), default_output=format_output( "Setting brightness to {brightness} and color temperature to {cct}" ), ) def set_brightness_and_color_temperature(self, brightness: int, cct: int): """Set brightness level and the correlated color temperature.""" if brightness < 1 or brightness > 100: raise CeilException("Invalid brightness: %s" % brightness) if cct < 1 or cct > 100: raise CeilException("Invalid color temperature: %s" % cct) return self.send("set_bricct", [brightness, cct]) @command( click.argument("seconds", type=int), default_output=format_output( "Setting delayed turn off to {seconds} seconds"), ) def delay_off(self, seconds: int): """Turn off delay in seconds.""" if seconds < 1: raise CeilException("Invalid value for a delayed turn off: %s" % seconds) return self.send("delay_off", [seconds]) @command( click.argument("number", type=int), default_output=format_output("Setting fixed scene to {number}"), ) def set_scene(self, number: int): """Set a fixed scene (1-4).""" if number < 1 or number > 4: raise CeilException("Invalid fixed scene number: %s" % number) return self.send("apply_fixed_scene", [number]) @command(default_output=format_output("Turning on smart night light")) def smart_night_light_on(self): """Smart Night Light On.""" return self.send("enable_bl", [1]) @command(default_output=format_output("Turning off smart night light")) def smart_night_light_off(self): """Smart Night Light off.""" return self.send("enable_bl", [0]) @command( default_output=format_output("Turning on automatic color temperature")) def automatic_color_temperature_on(self): """Automatic color temperature on.""" return self.send("enable_ac", [1]) @command( default_output=format_output("Turning off automatic color temperature") ) def automatic_color_temperature_off(self): """Automatic color temperature off.""" return self.send("enable_ac", [0])
def images_command(): pass OPENEDX_IMAGES = ["openedx", "forum", "notes", "xqueue", "android"] VENDOR_IMAGES = [ "elasticsearch", "memcached", "mongodb", "mysql", "nginx", "rabbitmq", "smtp", ] argument_openedx_image = click.argument("image", type=click.Choice(["all"] + OPENEDX_IMAGES)) argument_image = click.argument("image", type=click.Choice(["all"] + OPENEDX_IMAGES + VENDOR_IMAGES)) @click.command( short_help="Build docker images", help="Build the docker images necessary for an Open edX platform.", ) @opts.root @argument_openedx_image @click.option("--no-cache", is_flag=True, help="Do not use cache when building the image")
class AlarmClock(Device): """Implementation of Xiao AI Smart Alarm Clock. Note, this device is not very responsive to the requests, so it may take several seconds /tries to get an answer. """ _supported_models = ["zimi.clock.myk01"] @command() def get_config_version(self): """ # values unknown {'result': [4], 'id': 203} :return: """ return self.send("get_config_version", ["audio"]) @command() def clock_system(self) -> HourlySystem: """Returns either 12 or 24 depending on which system is in use.""" return HourlySystem(self.send("get_hourly_system")[0]) @command(click.argument("brightness", type=EnumType(HourlySystem))) def set_hourly_system(self, hs: HourlySystem): return self.send("set_hourly_system", [hs.value]) @command() def get_button_light(self): """Get button's light state.""" # ['normal', 'mute', 'offline'] or [] return self.send("get_enabled_key_light") @command(click.argument("on", type=bool)) def set_button_light(self, on): """Enable or disable the button light.""" if on: return self.send("enable_key_light") == ["OK"] else: return self.send("disable_key_light") == ["OK"] @command() def volume(self) -> int: """Return the volume. -> 192.168.0.128 data= {"id":251,"method":"set_volume","params":[17]} <- 192.168.0.57 data= {"result":["OK"],"id":251} """ return int(self.send("get_volume")[0]) @command(click.argument("volume", type=int)) def set_volume(self, volume): """Set volume [1,100].""" return self.send("set_volume", [volume]) == ["OK"] @command( click.argument("alarm_type", type=EnumType(AlarmType), default=AlarmType.Alarm.name)) def get_ring(self, alarm_type: AlarmType): """Get current ring tone settings.""" return RingTone( self.send("get_ring", [{ "type": alarm_type.value }]).pop()) @command( click.argument("alarm_type", type=EnumType(AlarmType)), click.argument("tone", type=EnumType(Tone)), ) def set_ring(self, alarm_type: AlarmType, ring: RingTone): """Set alarm tone. -> 192.168.0.128 data= {"id":236,"method":"set_ring", "params":[{"ringtone":"a1.mp3","smart_clock":"","type":"alarm"}]} <- 192.168.0.57 data= {"result":["OK"],"id":236} """ raise NotImplementedError() # return self.send("set_ring", ) == ["OK"] @command() def night_mode(self): """Get night mode status. -> 192.168.0.128 data= {"id":234,"method":"get_night_mode","params":[]} <- 192.168.0.57 data= {"result":[0],"id":234} """ return Nightmode(self.send("get_night_mode")) @command() def set_night_mode(self): """Set the night mode. # enable -> 192.168.0.128 data= {"id":248,"method":"set_night_mode", "params":[1,"21:00","6:00"]} <- 192.168.0.57 data= {"result":["OK"],"id":248} # disable -> 192.168.0.128 data= {"id":249,"method":"set_night_mode", "params":[0,"21:00","6:00"]} <- 192.168.0.57 data= {"result":["OK"],"id":249} """ raise NotImplementedError() @command() def near_wakeup(self): """Status for near wakeup. -> 192.168.0.128 data= {"id":235,"method":"get_near_wakeup_status", "params":[]} <- 192.168.0.57 data= {"result":["disable"],"id":235} # setters -> 192.168.0.128 data= {"id":254,"method":"set_near_wakeup_status", "params":["enable"]} <- 192.168.0.57 data= {"result":["OK"],"id":254} -> 192.168.0.128 data= {"id":255,"method":"set_near_wakeup_status", "params":["disable"]} <- 192.168.0.57 data= {"result":["OK"],"id":255} """ return self.send("get_near_wakeup_status") @command() def countdown(self): """ -> 192.168.0.128 data= {"id":258,"method":"get_count_down_v2","params":[]} """ return self.send("get_count_down_v2") @command() def alarmops(self): """ NOTE: the alarm_ops method is the one used to create, query and delete all types of alarms (reminders, alarms, countdowns). -> 192.168.0.128 data= {"id":263,"method":"alarm_ops", "params":{"operation":"create","data":[ {"type":"alarm","event":"testlabel","reminder":"","smart_clock":0, "ringtone":"a2.mp3","volume":100,"circle":"once","status":"on", "repeat_ringing":0,"delete_datetime":1564291980000, "disable_datetime":"","circle_extra":"", "datetime":1564291980000} ],"update_datetime":1564205639326}} <- 192.168.0.57 data= {"result":[{"id":1,"ack":"OK"}],"id":263} # query per index, starts from 0 instead of 1 as the ids it seems -> 192.168.0.128 data= {"id":264,"method":"alarm_ops", "params":{"operation":"query","req_type":"alarm", "update_datetime":1564205639593,"index":0}} <- 192.168.0.57 data= {"result": [0,[ {"i":"1","c":"once","d":"2019-07-28T13:33:00+0800","s":"on", "n":"testlabel","a":"a2.mp3","dd":1} ], "America/New_York" ],"id":264} # result [code, list of alarms, timezone] -> 192.168.0.128 data= {"id":265,"method":"alarm_ops", "params":{"operation":"query","index":0,"update_datetime":1564205639596, "req_type":"reminder"}} <- 192.168.0.57 data= {"result":[0,[],"America/New_York"],"id":265} """ raise NotImplementedError() @command(click.argument("url")) def start_countdown(self, url): """Start countdown timer playing the given media. {"id":354,"method":"alarm_ops", "params":{"operation":"create","update_datetime":1564206432733, "data":[{"type":"timer", "background":"http://host.invalid/testfile.mp3", "offset":1800, "circle":"once", "volume":100, "datetime":1564208232733}]}} """ current_ts = int(time.time() * 1000) payload = { "operation": "create", "update_datetime": current_ts, "data": [{ "type": "timer", "background": "http://url_here_for_mp3", "offset": 30, "circle": "once", "volume": 30, "datetime": current_ts, }], } return self.send("alarm_ops", payload) @command() def query(self): """ -> 192.168.0.128 data= {"id":227,"method":"alarm_ops","params": {"operation":"query","index":0,"update_datetime":1564205198413,"req_type":"reminder"}} """ payload = { "operation": "query", "index": 0, "update_datetime": int(time.time() * 1000), "req_type": "timer", } return self.send("alarm_ops", payload) @command() def cancel(self): """Cancel alarm of the defined type. "params":{"operation":"cancel","update_datetime":1564206332603,"data":[{"type":"timer"}]}} """ import time payload = { "operation": "pause", "update_datetime": int(time.time() * 1000), "data": [{ "type": "timer" }], } return self.send("alarm_ops", payload)
class ChuangmiPlug(Device): """Main class representing the Chuangmi Plug V1 and V3.""" def __init__(self, ip: str = None, token: str = None, start_id: int = 0, debug: int = 0, lazy_discover: bool = True, model: str = MODEL_CHUANGMI_PLUG_M1) -> None: super().__init__(ip, token, start_id, debug, lazy_discover) if model in AVAILABLE_PROPERTIES: self.model = model else: self.model = MODEL_CHUANGMI_PLUG_M1 @command(default_output=format_output( "", "Power: {result.power}\n" "USB Power: {result.usb_power}\n" "Temperature: {result.temperature} °C\n" "Load power: {result.load_power}\n" "WiFi LED: {result.wifi_led}\n")) def status(self) -> ChuangmiPlugStatus: """Retrieve properties.""" properties = AVAILABLE_PROPERTIES[self.model].copy() values = self.send("get_prop", properties) properties_count = len(properties) values_count = len(values) if properties_count != values_count: _LOGGER.debug( "Count (%s) of requested properties does not match the " "count (%s) of received values.", properties_count, values_count) if self.model == MODEL_CHUANGMI_PLUG_V3: load_power = self.send("get_power", []) # Response: [300] if len(load_power) == 1: properties.append('load_power') values.append(load_power[0] * 0.01) return ChuangmiPlugStatus( defaultdict(lambda: None, zip(properties, values))) @command( default_output=format_output("Powering on"), ) def on(self): """Power on.""" if self.model == MODEL_CHUANGMI_PLUG_V1: return self.send("set_on", []) return self.send("set_power", ["on"]) @command( default_output=format_output("Powering off"), ) def off(self): """Power off.""" if self.model == MODEL_CHUANGMI_PLUG_V1: return self.send("set_off", []) return self.send("set_power", ["off"]) @command( default_output=format_output("Powering USB on"), ) def usb_on(self): """Power on.""" return self.send("set_usb_on", []) @command( default_output=format_output("Powering USB off"), ) def usb_off(self): """Power off.""" return self.send("set_usb_off", []) @command( click.argument("wifi_led", type=bool), default_output=format_output(lambda wifi_led: "Turning on WiFi LED" if wifi_led else "Turning off WiFi LED")) def set_wifi_led(self, led: bool): """Set the wifi led on/off.""" if led: return self.send("set_wifi_led", ["on"]) else: return self.send("set_wifi_led", ["off"])
class AirConditionerMiot(MiotDevice): """Main class representing the air conditioner which uses MIoT protocol.""" mapping = _MAPPING @command(default_output=format_output( "", "Power: {result.power}\n" "Mode: {result.mode}\n" "Target Temperature: {result.target_temperature} ℃\n" "ECO Mode: {result.eco}\n" "Heater: {result.heater}\n" "Dryer: {result.dryer}\n" "Sleep Mode: {result.sleep_mode}\n" "Fan Speed: {result.fan_speed}\n" "Vertical Swing: {result.vertical_swing}\n" "Room Temperature: {result.temperature} ℃\n" "Buzzer: {result.buzzer}\n" "LED: {result.led}\n" "Electricity: {result.electricity}kWh\n" "Clean: {result.clean}\n" "Running Duration: {result.total_running_duration}\n" "Fan percent: {result.fan_speed_percent}\n" "Timer: {result.timer}\n", )) def status(self) -> AirConditionerMiotStatus: """Retrieve properties.""" return AirConditionerMiotStatus({ prop["did"]: prop["value"] if prop["code"] == 0 else None for prop in self.get_properties_for_mapping() }) @command(default_output=format_output("Powering on")) def on(self): """Power on.""" return self.set_property("power", True) @command(default_output=format_output("Powering off")) def off(self): """Power off.""" return self.set_property("power", False) @command( click.argument("mode", type=EnumType(OperationMode)), default_output=format_output( "Setting operation mode to '{mode.value}'"), ) def set_mode(self, mode: OperationMode): """Set operation mode.""" return self.set_property("mode", mode.value) @command( click.argument("target_temperature", type=float), default_output=format_output( "Setting target temperature to {target_temperature}"), ) def set_target_temperature(self, target_temperature: float): """Set target temperature in Celsius.""" if (target_temperature < 16.0 or target_temperature > 31.0 or target_temperature % 0.5 != 0): raise AirConditionerMiotException( "Invalid target temperature: %s" % target_temperature) return self.set_property("target_temperature", target_temperature) @command( click.argument("eco", type=bool), default_output=format_output(lambda eco: "Turning on ECO mode" if eco else "Turning off ECO mode"), ) def set_eco(self, eco: bool): """Turn ECO mode on/off.""" return self.set_property("eco", eco) @command( click.argument("heater", type=bool), default_output=format_output(lambda heater: "Turning on heater" if heater else "Turning off heater"), ) def set_heater(self, heater: bool): """Turn aux heater mode on/off.""" return self.set_property("heater", heater) @command( click.argument("dryer", type=bool), default_output=format_output(lambda dryer: "Turning on dryer" if dryer else "Turning off dryer"), ) def set_dryer(self, dryer: bool): """Turn aux dryer mode on/off.""" return self.set_property("dryer", dryer) @command( click.argument("sleep_mode", type=bool), default_output=format_output( lambda sleep_mode: "Turning on sleep mode" if sleep_mode else "Turning off sleep mode"), ) def set_sleep_mode(self, sleep_mode: bool): """Turn sleep mode on/off.""" return self.set_property("sleep_mode", sleep_mode) @command( click.argument("fan_speed", type=EnumType(FanSpeed)), default_output=format_output("Setting fan speed to {fan_speed}"), ) def set_fan_speed(self, fan_speed: FanSpeed): """Set fan speed.""" return self.set_property("fan_speed", fan_speed.value) @command( click.argument("vertical_swing", type=bool), default_output=format_output( lambda vertical_swing: "Turning on vertical swing" if vertical_swing else "Turning off vertical swing"), ) def set_vertical_swing(self, vertical_swing: bool): """Turn vertical swing on/off.""" return self.set_property("vertical_swing", vertical_swing) @command( click.argument("led", type=bool), default_output=format_output(lambda led: "Turning on LED" if led else "Turning off LED"), ) def set_led(self, led: bool): """Turn led on/off.""" return self.set_property("led", led) @command( click.argument("buzzer", type=bool), default_output=format_output(lambda buzzer: "Turning on buzzer" if buzzer else "Turning off buzzer"), ) def set_buzzer(self, buzzer: bool): """Set buzzer on/off.""" return self.set_property("buzzer", buzzer) @command( click.argument("percent", type=int), default_output=format_output("Setting fan percent to {percent}%"), ) def set_fan_speed_percent(self, fan_speed_percent): """Set fan speed in percent, should be between 1 to 100 or 101(auto).""" if fan_speed_percent < 1 or fan_speed_percent > 101: raise AirConditionerMiotException("Invalid fan percent: %s" % fan_speed_percent) return self.set_property("fan_speed_percent", fan_speed_percent) @command( click.argument("minutes", type=int), click.argument("delay_on", type=bool), default_output=format_output( lambda minutes, delay_on: "Setting timer to delay on after " + str( minutes) + " minutes" if delay_on else "Setting timer to delay off after " + str(minutes) + " minutes"), ) def set_timer(self, minutes, delay_on): """Set countdown timer minutes and if it would be turned on after timeout. Set minutes to 0 would disable the timer. """ return self.set_property( "timer", ",".join(["1", str(minutes), str(int(delay_on))])) @command( click.argument("clean", type=bool), default_output=format_output(lambda clean: "Begin auto cleanning" if clean else "Abort auto cleaning"), ) def set_clean(self, clean): """Start or abort clean mode.""" return self.set_property("clean", str(int(clean)))
class FanMiot(MiotDevice): def __init__( self, ip: str = None, token: str = None, start_id: int = 0, debug: int = 0, lazy_discover: bool = True, model: str = MODEL_FAN_P10, ) -> None: if model not in MIOT_MAPPING: raise FanException("Invalid FanMiot model: %s" % model) self.model = model super().__init__(MIOT_MAPPING[model], ip, token, start_id, debug, lazy_discover) @command(default_output=format_output( "", "Power: {result.power}\n" "Operation mode: {result.mode}\n" "Speed: {result.speed}\n" "Oscillate: {result.oscillate}\n" "Angle: {result.angle}\n" "LED: {result.led}\n" "Buzzer: {result.buzzer}\n" "Child lock: {result.child_lock}\n" "Power-off time: {result.delay_off_countdown}\n", )) def status(self) -> FanStatusMiot: """Retrieve properties.""" return FanStatusMiot({ prop["did"]: prop["value"] if prop["code"] == 0 else None for prop in self.get_properties_for_mapping() }) @command(default_output=format_output("Powering on")) def on(self): """Power on.""" return self.set_property("power", True) @command(default_output=format_output("Powering off")) def off(self): """Power off.""" return self.set_property("power", False) @command( click.argument("mode", type=EnumType(OperationMode)), default_output=format_output("Setting mode to '{mode.value}'"), ) def set_mode(self, mode: OperationMode): """Set mode.""" return self.set_property("mode", OperationModeMiot[mode.name].value) @command( click.argument("speed", type=int), default_output=format_output("Setting speed to {speed}"), ) def set_speed(self, speed: int): """Set speed.""" if speed < 0 or speed > 100: raise FanException("Invalid speed: %s" % speed) return self.set_property("fan_speed", speed) @command( click.argument("angle", type=int), default_output=format_output("Setting angle to {angle}"), ) def set_angle(self, angle: int): """Set the oscillation angle.""" if angle not in [30, 60, 90, 120, 140]: raise FanException( "Unsupported angle. Supported values: 30, 60, 90, 120, 140") return self.set_property("swing_mode_angle", angle) @command( click.argument("oscillate", type=bool), default_output=format_output( lambda oscillate: "Turning on oscillate" if oscillate else "Turning off oscillate"), ) def set_oscillate(self, oscillate: bool): """Set oscillate on/off.""" if oscillate: return self.set_property("swing_mode", True) else: return self.set_property("swing_mode", False) @command( click.argument("led", type=bool), default_output=format_output(lambda led: "Turning on LED" if led else "Turning off LED"), ) def set_led(self, led: bool): """Turn led on/off.""" if led: return self.set_property("light", True) else: return self.set_property("light", False) @command( click.argument("buzzer", type=bool), default_output=format_output(lambda buzzer: "Turning on buzzer" if buzzer else "Turning off buzzer"), ) def set_buzzer(self, buzzer: bool): """Set buzzer on/off.""" if buzzer: return self.set_property("buzzer", True) else: return self.set_property("buzzer", False) @command( click.argument("lock", type=bool), default_output=format_output(lambda lock: "Turning on child lock" if lock else "Turning off child lock"), ) def set_child_lock(self, lock: bool): """Set child lock on/off.""" return self.set_property("child_lock", lock) @command( click.argument("minutes", type=int), default_output=format_output( "Setting delayed turn off to {minutes} minutes"), ) def delay_off(self, minutes: int): """Set delay off minutes.""" if minutes < 0: raise FanException("Invalid value for a delayed turn off: %s" % minutes) return self.set_property("power_off_time", minutes) @command( click.argument("direction", type=EnumType(MoveDirection)), default_output=format_output("Rotating the fan to the {direction}"), ) def set_rotate(self, direction: MoveDirection): return self.set_property("set_move", [direction.value])
help='Value for parameterized action.') option_email = click.option('-e', '--email', multiple=True, default=[], metavar='EMAIL', help='User email address(es).') option_role = click.option('-r', '--role', multiple=True, default=[], metavar='ROLE', help='Role name(s).') argument_action = click.argument('action', callback=process_action, nargs=1, required=True, metavar='ACTION') argument_user = click.argument('user', callback=process_email, nargs=1, required=True, metavar='EMAIL') argument_role = click.argument('role', callback=process_role, nargs=1, required=True, metavar='ROLE') #
file_list = find_files(folders, supported_formats) music_files = list(file_list) files = [] def worker(file): try: m = File(file[1], file[0]) files.append(m) except KeyboardInterrupt as e: logger.error(f'interrupted : {e}') raise except OSError as e: logger.error(e) Conf.parallel(worker, music_files) return files folders_argument = click.argument( 'folders', type=click.Path(exists=True, file_okay=False), nargs=-1, callback=config_list, ) folder_argument = click.argument( 'folder', type=click.Path(exists=True, file_okay=False), )
import click from .click_utils import ( CommaSeparatedText, Dictionary, valid_limit, valid_parameter_limits, mutually_exclusive_with, required_by, ) COMMON_OPTIONS = { 'input': [ click.argument( 'input_obj', metavar='<input_obj>', type=click.Path(exists=True, dir_okay=False), ), click.option( '--input-format', '-f', type=click.Choice(['anndata', 'loom']), default='anndata', show_default=True, help='Input object format.', ), ], 'output': [ click.argument( 'output_obj', metavar='<output_obj>',
return from_pickle(file) cache_path = get_corresponding_pickle_path(path) if not os.path.exists(cache_path): click.echo( 'The BEL script {path} has not yet been compiled. First, try running the following command:\n\n ' 'pybel compile {path}\n'.format(path=path), ) sys.exit(1) return from_pickle(cache_path) graph_pickle_argument = click.argument( 'graph', metavar='path', type=click.File('rb'), callback=_from_pickle_callback, ) graph_argument = click.argument( 'graph', metavar='path', callback=lambda _, __, path: load(path), ) @with_plugins(iter_entry_points('pybel.cli_plugins')) @click.group(help="PyBEL CLI on {}".format(sys.executable)) @click.version_option() @connection_option @click.pass_context
class AirPurifier(Device): """Main class representing the air purifier.""" @command(default_output=format_output( "", "Power: {result.power}\n" "AQI: {result.aqi} μg/m³\n" "Average AQI: {result.average_aqi} μg/m³\n" "Temperature: {result.temperature} °C\n" "Humidity: {result.humidity} %\n" "Mode: {result.mode.value}\n" "LED: {result.led}\n" "LED brightness: {result.led_brightness}\n" "Illuminance: {result.illuminance} lx\n" "Buzzer: {result.buzzer}\n" "Child lock: {result.child_lock}\n" "Favorite level: {result.favorite_level}\n" "Filter life remaining: {result.filter_life_remaining} %\n" "Filter hours used: {result.filter_hours_used}\n" "Use time: {result.use_time} s\n" "Purify volume: {result.purify_volume} m³\n" "Motor speed: {result.motor_speed} rpm\n" "Motor 2 speed: {result.motor2_speed} rpm\n" "Sound volume: {result.volume} %\n" "Filter RFID product id: {result.filter_rfid_product_id}\n" "Filter RFID tag: {result.filter_rfid_tag}\n" "Filter type: {result.filter_type}\n" "Learn mode: {result.learn_mode}\n" "Sleep mode: {result.sleep_mode}\n" "Sleep time: {result.sleep_time}\n" "Sleep mode learn count: {result.sleep_mode_learn_count}\n" "AQI sensor enabled on power off: {result.auto_detect}\n", )) def status(self) -> AirPurifierStatus: """Retrieve properties.""" properties = [ "power", "aqi", "average_aqi", "humidity", "temp_dec", "mode", "favorite_level", "filter1_life", "f1_hour_used", "use_time", "motor1_speed", "motor2_speed", "purify_volume", "f1_hour", "led", # Second request "led_b", "bright", "buzzer", "child_lock", "volume", "rfid_product_id", "rfid_tag", "act_sleep", "sleep_mode", "sleep_time", "sleep_data_num", "app_extra", "act_det", "button_pressed", ] # A single request is limited to 16 properties. Therefore the # properties are divided into multiple requests _props = properties.copy() values = [] while _props: values.extend(self.send("get_prop", _props[:15])) _props[:] = _props[15:] properties_count = len(properties) values_count = len(values) if properties_count != values_count: _LOGGER.debug( "Count (%s) of requested properties does not match the " "count (%s) of received values.", properties_count, values_count, ) return AirPurifierStatus( defaultdict(lambda: None, zip(properties, values))) @command(default_output=format_output("Powering on")) def on(self): """Power on.""" return self.send("set_power", ["on"]) @command(default_output=format_output("Powering off")) def off(self): """Power off.""" return self.send("set_power", ["off"]) @command( click.argument("mode", type=EnumType(OperationMode, False)), default_output=format_output("Setting mode to '{mode.value}'"), ) def set_mode(self, mode: OperationMode): """Set mode.""" return self.send("set_mode", [mode.value]) @command( click.argument("level", type=int), default_output=format_output("Setting favorite level to {level}"), ) def set_favorite_level(self, level: int): """Set favorite level.""" if level < 0 or level > 17: raise AirPurifierException("Invalid favorite level: %s" % level) # Possible alternative property: set_speed_favorite # Set the favorite level used when the mode is `favorite`, # should be between 0 and 17. return self.send("set_level_favorite", [level]) # 0 ... 17 @command( click.argument("brightness", type=EnumType(LedBrightness, False)), default_output=format_output("Setting LED brightness to {brightness}"), ) def set_led_brightness(self, brightness: LedBrightness): """Set led brightness.""" return self.send("set_led_b", [brightness.value]) @command( click.argument("led", type=bool), default_output=format_output(lambda led: "Turning on LED" if led else "Turning off LED"), ) def set_led(self, led: bool): """Turn led on/off.""" if led: return self.send("set_led", ["on"]) else: return self.send("set_led", ["off"]) @command( click.argument("buzzer", type=bool), default_output=format_output(lambda buzzer: "Turning on buzzer" if buzzer else "Turning off buzzer"), ) def set_buzzer(self, buzzer: bool): """Set buzzer on/off.""" if buzzer: return self.send("set_buzzer", ["on"]) else: return self.send("set_buzzer", ["off"]) @command( click.argument("lock", type=bool), default_output=format_output(lambda lock: "Turning on child lock" if lock else "Turning off child lock"), ) def set_child_lock(self, lock: bool): """Set child lock on/off.""" if lock: return self.send("set_child_lock", ["on"]) else: return self.send("set_child_lock", ["off"]) @command( click.argument("volume", type=int), default_output=format_output("Setting favorite level to {volume}"), ) def set_volume(self, volume: int): """Set volume of sound notifications [0-100].""" if volume < 0 or volume > 100: raise AirPurifierException("Invalid volume: %s" % volume) return self.send("set_volume", [volume]) @command( click.argument("learn_mode", type=bool), default_output=format_output( lambda learn_mode: "Turning on learn mode" if learn_mode else "Turning off learn mode"), ) def set_learn_mode(self, learn_mode: bool): """Set the Learn Mode on/off.""" if learn_mode: return self.send("set_act_sleep", ["single"]) else: return self.send("set_act_sleep", ["close"]) @command( click.argument("auto_detect", type=bool), default_output=format_output( lambda auto_detect: "Turning on auto detect" if auto_detect else "Turning off auto detect"), ) def set_auto_detect(self, auto_detect: bool): """Set auto detect on/off. It's a feature of the AirPurifier V1 & V3""" if auto_detect: return self.send("set_act_det", ["on"]) else: return self.send("set_act_det", ["off"]) @command( click.argument("value", type=int), default_output=format_output("Setting extra to {value}"), ) def set_extra_features(self, value: int): """Storage register to enable extra features at the app. app_extra=1 unlocks a turbo mode supported feature """ if value < 0: raise AirPurifierException("Invalid app extra value: %s" % value) return self.send("set_app_extra", [value]) @command(default_output=format_output("Resetting filter")) def reset_filter(self): """Resets filter hours used and remaining life.""" return self.send("reset_filter1")
class Vacuum(Device): """Main class representing the vacuum.""" def __init__(self, ip: str, token: str = None, start_id: int = 0, debug: int = 0) -> None: super().__init__(ip, token, start_id, debug) self.manual_seqnum = -1 self.model = None self._fanspeeds = FanspeedV1 @command() def start(self): """Start cleaning.""" return self.send("app_start") @command() def stop(self): """Stop cleaning. Note, prefer 'pause' instead of this for wider support. Some newer vacuum models do not support this command. """ return self.send("app_stop") @command() def spot(self): """Start spot cleaning.""" return self.send("app_spot") @command() def pause(self): """Pause cleaning.""" return self.send("app_pause") @command() def resume_or_start(self): """A shortcut for resuming or starting cleaning.""" status = self.status() if status.in_zone_cleaning and (status.is_paused or status.got_error): return self.resume_zoned_clean() return self.start() @command() def home(self): """Stop cleaning and return home.""" self.send("app_pause") return self.send("app_charge") @command(click.argument("x_coord", type=int), click.argument("y_coord", type=int)) def goto(self, x_coord: int, y_coord: int): """Go to specific target. :param int x_coord: x coordinate :param int y_coord: y coordinate""" return self.send("app_goto_target", [x_coord, y_coord]) @command(click.argument("zones", type=LiteralParamType(), required=True)) def zoned_clean(self, zones: List): """Clean zones. :param List zones: List of zones to clean: [[x1,y1,x2,y2, iterations],[x1,y1,x2,y2, iterations]]""" return self.send("app_zoned_clean", zones) @command() def resume_zoned_clean(self): """Resume zone cleaning after being paused.""" return self.send("resume_zoned_clean") @command() def manual_start(self): """Start manual control mode.""" self.manual_seqnum = 0 return self.send("app_rc_start") @command() def manual_stop(self): """Stop manual control mode.""" self.manual_seqnum = 0 return self.send("app_rc_end") MANUAL_ROTATION_MAX = 180 MANUAL_ROTATION_MIN = -MANUAL_ROTATION_MAX MANUAL_VELOCITY_MAX = 0.3 MANUAL_VELOCITY_MIN = -MANUAL_VELOCITY_MAX MANUAL_DURATION_DEFAULT = 1500 @command( click.argument("rotation", type=int), click.argument("velocity", type=float), click.argument("duration", type=int, required=False, default=MANUAL_DURATION_DEFAULT), ) def manual_control_once(self, rotation: int, velocity: float, duration: int = MANUAL_DURATION_DEFAULT): """Starts the remote control mode and executes the action once before deactivating the mode.""" number_of_tries = 3 self.manual_start() while number_of_tries > 0: if self.status().state_code == 7: time.sleep(5) self.manual_control(rotation, velocity, duration) time.sleep(5) return self.manual_stop() time.sleep(2) number_of_tries -= 1 @command( click.argument("rotation", type=int), click.argument("velocity", type=float), click.argument("duration", type=int, required=False, default=MANUAL_DURATION_DEFAULT), ) def manual_control(self, rotation: int, velocity: float, duration: int = MANUAL_DURATION_DEFAULT): """Give a command over manual control interface.""" if rotation < self.MANUAL_ROTATION_MIN or rotation > self.MANUAL_ROTATION_MAX: raise DeviceException( "Given rotation is invalid, should be ]%s, %s[, was %s" % (self.MANUAL_ROTATION_MIN, self.MANUAL_ROTATION_MAX, rotation)) if velocity < self.MANUAL_VELOCITY_MIN or velocity > self.MANUAL_VELOCITY_MAX: raise DeviceException( "Given velocity is invalid, should be ]%s, %s[, was: %s" % (self.MANUAL_VELOCITY_MIN, self.MANUAL_VELOCITY_MAX, velocity)) self.manual_seqnum += 1 params = { "omega": round(math.radians(rotation), 1), "velocity": velocity, "duration": duration, "seqnum": self.manual_seqnum, } self.send("app_rc_move", [params]) @command() def status(self) -> VacuumStatus: """Return status of the vacuum.""" return VacuumStatus(self.send("get_status")[0]) def enable_log_upload(self): raise NotImplementedError("unknown parameters") # return self.send("enable_log_upload") @command() def log_upload_status(self): # {"result": [{"log_upload_status": 7}], "id": 1} return self.send("get_log_upload_status") @command() def consumable_status(self) -> ConsumableStatus: """Return information about consumables.""" return ConsumableStatus(self.send("get_consumable")[0]) @command(click.argument("consumable", type=Consumable)) def consumable_reset(self, consumable: Consumable): """Reset consumable information.""" return self.send("reset_consumable", [consumable.value]) @command() def map(self): """Return map token.""" # returns ['retry'] without internet return self.send("get_map_v1") @command(click.argument("start", type=bool)) def edit_map(self, start): """Start map editing?""" if start: return self.send("start_edit_map")[0] == "ok" else: return self.send("end_edit_map")[0] == "ok" @command(click.option("--version", default=1)) def fresh_map(self, version): """Return fresh map?""" if version == 1: return self.send("get_fresh_map") elif version == 2: return self.send("get_fresh_map_v2") else: raise VacuumException("Unknown map version: %s" % version) @command(click.option("--version", default=1)) def persist_map(self, version): """Return fresh map?""" if version == 1: return self.send("get_persist_map") elif version == 2: return self.send("get_persist_map_v2") else: raise VacuumException("Unknown map version: %s" % version) @command( click.argument("x1", type=int), click.argument("y1", type=int), click.argument("x2", type=int), click.argument("y2", type=int), ) def create_software_barrier(self, x1, y1, x2, y2): """Create software barrier (gen2 only?). NOTE: Multiple nogo zones and barriers could be added by passing a list of them to save_map. Requires new fw version. 3.3.9_001633+? """ # First parameter indicates the type, 1 = barrier payload = [1, x1, y1, x2, y2] return self.send("save_map", payload)[0] == "ok" @command( click.argument("x1", type=int), click.argument("y1", type=int), click.argument("x2", type=int), click.argument("y2", type=int), click.argument("x3", type=int), click.argument("y3", type=int), click.argument("x4", type=int), click.argument("y4", type=int), ) def create_nogo_zone(self, x1, y1, x2, y2, x3, y3, x4, y4): """Create a rectangular no-go zone (gen2 only?). NOTE: Multiple nogo zones and barriers could be added by passing a list of them to save_map. Requires new fw version. 3.3.9_001633+? """ # First parameter indicates the type, 0 = zone payload = [0, x1, y1, x2, y2, x3, y3, x4, y4] return self.send("save_map", payload)[0] == "ok" @command(click.argument("enable", type=bool)) def enable_lab_mode(self, enable): """Enable persistent maps and software barriers. This is required to use create_nogo_zone and create_software_barrier commands.""" return self.send("set_lab_status", int(enable))["ok"] @command() def clean_history(self) -> CleaningSummary: """Return generic cleaning history.""" return CleaningSummary(self.send("get_clean_summary")) @command() def last_clean_details(self) -> Optional[CleaningDetails]: """Return details from the last cleaning. Returns None if there has been no cleanups.""" history = self.clean_history() if not history.ids: return None last_clean_id = history.ids.pop(0) return self.clean_details(last_clean_id, return_list=False) @command( click.argument("id_", type=int, metavar="ID"), click.argument("return_list", type=bool, default=False), ) def clean_details( self, id_: int, return_list=True ) -> Union[List[CleaningDetails], Optional[CleaningDetails]]: """Return details about specific cleaning.""" details = self.send("get_clean_record", [id_]) if not details: _LOGGER.warning("No cleaning record found for id %s" % id_) return None if return_list: _LOGGER.warning("This method will be returning the details " "without wrapping them into a list in the " "near future. The current behavior can be " "kept by passing return_list=True and this " "warning will be removed when the default gets " "changed.") return [CleaningDetails(entry) for entry in details] if len(details) > 1: _LOGGER.warning("Got multiple clean details, returning the first") res = CleaningDetails(details.pop()) return res @command() def find(self): """Find the robot.""" return self.send("find_me", [""]) @command() def timer(self) -> List[Timer]: """Return a list of timers.""" timers = list() timezone = pytz.timezone(self.timezone()) for rec in self.send("get_timer", [""]): try: timers.append(Timer(rec, timezone=timezone)) except Exception as ex: _LOGGER.warning("Unable to add timer for %s: %s", rec, ex) return timers @command( click.argument("cron"), click.argument("command", required=False, default=""), click.argument("parameters", required=False, default=""), ) def add_timer(self, cron: str, command: str, parameters: str): """Add a timer. :param cron: schedule in cron format :param command: ignored by the vacuum. :param parameters: ignored by the vacuum.""" import time ts = int(round(time.time() * 1000)) return self.send("set_timer", [[str(ts), [cron, [command, parameters]]]]) @command(click.argument("timer_id", type=int)) def delete_timer(self, timer_id: int): """Delete a timer with given ID. :param int timer_id: Timer ID""" return self.send("del_timer", [str(timer_id)]) @command(click.argument("timer_id", type=int), click.argument("mode", type=TimerState)) def update_timer(self, timer_id: int, mode: TimerState): """Update a timer with given ID. :param int timer_id: Timer ID :param TimerStae mode: either On or Off""" if mode != TimerState.On and mode != TimerState.Off: raise DeviceException("Only 'On' or 'Off' are allowed") return self.send("upd_timer", [str(timer_id), mode.value]) @command() def dnd_status(self): """Returns do-not-disturb status.""" # {'result': [{'enabled': 1, 'start_minute': 0, 'end_minute': 0, # 'start_hour': 22, 'end_hour': 8}], 'id': 1} return DNDStatus(self.send("get_dnd_timer")[0]) @command( click.argument("start_hr", type=int), click.argument("start_min", type=int), click.argument("end_hr", type=int), click.argument("end_min", type=int), ) def set_dnd(self, start_hr: int, start_min: int, end_hr: int, end_min: int): """Set do-not-disturb. :param int start_hr: Start hour :param int start_min: Start minute :param int end_hr: End hour :param int end_min: End minute""" return self.send("set_dnd_timer", [start_hr, start_min, end_hr, end_min]) @command() def disable_dnd(self): """Disable do-not-disturb.""" return self.send("close_dnd_timer", [""]) @command(click.argument("speed", type=int)) def set_fan_speed(self, speed: int): """Set fan speed. :param int speed: Fan speed to set""" # speed = [38, 60 or 77] return self.send("set_custom_mode", [speed]) @command() def fan_speed(self): """Return fan speed.""" return self.send("get_custom_mode")[0] def _autodetect_model(self): """Detect the model of the vacuum. For the moment this is used only for the fanspeeds, but that could be extended to cover other supported features.""" try: info = self.info() self.model = info.model except (TypeError, DeviceInfoUnavailableException): # cloud-blocked vacuums will not return proper payloads self._fanspeeds = FanspeedV1 self.model = ROCKROBO_V1 _LOGGER.debug("Unable to query model, falling back to %s", self._fanspeeds) return _LOGGER.info("model: %s", self.model) if info.model == ROCKROBO_V1: _LOGGER.debug("Got robov1, checking for firmware version") fw_version = info.firmware_version version, build = fw_version.split("_") version = tuple(map(int, version.split("."))) if version >= (3, 5, 7): self._fanspeeds = FanspeedV2 else: self._fanspeeds = FanspeedV1 elif info.model == "roborock.vacuum.e2": self._fanspeeds = FanspeedE2 else: self._fanspeeds = FanspeedV2 _LOGGER.debug("Using new fanspeed mapping %s for %s", self._fanspeeds, info.model) @command() def fan_speed_presets(self) -> Dict[str, int]: """Return dictionary containing supported fan speeds.""" if self.model is None: self._autodetect_model() return {x.name: x.value for x in list(self._fanspeeds)} @command() def sound_info(self): """Get voice settings.""" return SoundStatus(self.send("get_current_sound")[0]) @command( click.argument("url"), click.argument("md5sum"), click.argument("sound_id", type=int), ) def install_sound(self, url: str, md5sum: str, sound_id: int): """Install sound from the given url.""" payload = {"url": url, "md5": md5sum, "sid": int(sound_id)} return SoundInstallStatus(self.send("dnld_install_sound", payload)[0]) @command() def sound_install_progress(self): """Get sound installation progress.""" return SoundInstallStatus(self.send("get_sound_progress")[0]) @command() def sound_volume(self) -> int: """Get sound volume.""" return self.send("get_sound_volume")[0] @command(click.argument("vol", type=int)) def set_sound_volume(self, vol: int): """Set sound volume [0-100].""" return self.send("change_sound_volume", [vol]) @command() def test_sound_volume(self): """Test current sound volume.""" return self.send("test_sound_volume") @command() def serial_number(self): """Get serial number.""" serial = self.send("get_serial_number") if isinstance(serial, list): return serial[0]["serial_number"] return serial @command() def locale(self): """Return locale information.""" return self.send("app_get_locale") @command() def timezone(self): """Get the timezone.""" res = self.send("get_timezone")[0] if isinstance(res, dict): # Xiaowa E25 example # {'olson': 'Europe/Berlin', 'posix': 'CET-1CEST,M3.5.0,M10.5.0/3'} if "olson" not in res: raise VacuumException("Unsupported timezone format: %s" % res) return res["olson"] # Gen1 vacuum: ['Europe/Berlin'] return res def set_timezone(self, new_zone): """Set the timezone.""" return self.send("set_timezone", [new_zone])[0] == "ok" def configure_wifi(self, ssid, password, uid=0, timezone=None): """Configure the wifi settings.""" extra_params = {} if timezone is not None: now = datetime.datetime.now(pytz.timezone(timezone)) offset_as_float = now.utcoffset().total_seconds() / 60 / 60 extra_params["tz"] = timezone extra_params["gmt_offset"] = offset_as_float return super().configure_wifi(ssid, password, uid, extra_params) @command() def carpet_mode(self): """Get carpet mode settings""" return CarpetModeStatus(self.send("get_carpet_mode")[0]) @command( click.argument("enabled", required=True, type=bool), click.argument("stall_time", required=False, default=10, type=int), click.argument("low", required=False, default=400, type=int), click.argument("high", required=False, default=500, type=int), click.argument("integral", required=False, default=450, type=int), ) def set_carpet_mode( self, enabled: bool, stall_time: int = 10, low: int = 400, high: int = 500, integral: int = 450, ): """Set the carpet mode.""" click.echo("Setting carpet mode: %s" % enabled) data = { "enable": int(enabled), "stall_time": stall_time, "current_low": low, "current_high": high, "current_integral": integral, } return self.send("set_carpet_mode", [data])[0] == "ok" @command() def stop_zoned_clean(self): """Stop cleaning a zone.""" return self.send("stop_zoned_clean") @command() def stop_segment_clean(self): """Stop cleaning a segment.""" return self.send("stop_segment_clean") @command() def resume_segment_clean(self): """Resuming cleaning a segment.""" return self.send("resume_segment_clean") @command(click.argument("segments", type=LiteralParamType(), required=True)) def segment_clean(self, segments: List): """Clean segments. :param List segments: List of segments to clean: [16,17,18]""" return self.send("app_segment_clean", segments) @command() def get_room_mapping(self): """Retrieves a list of segments.""" return self.send("get_room_mapping") @command() def get_backup_maps(self): """Get backup maps.""" return self.send("get_recover_maps") @command(click.argument("id", type=int)) def use_backup_map(self, id: int): """Set backup map.""" click.echo("Setting the map %s as active" % id) return self.send("recover_map", [id]) @command() def get_segment_status(self): """Get the status of a segment.""" return self.send("get_segment_status") def name_segment(self): raise NotImplementedError("unknown parameters") # return self.send("name_segment") def merge_segment(self): raise NotImplementedError("unknown parameters") # return self.send("merge_segment") def split_segment(self): raise NotImplementedError("unknown parameters") # return self.send("split_segment") @command() def waterflow(self) -> WaterFlow: """Get water flow setting.""" return WaterFlow(self.send("get_water_box_custom_mode")[0]) @command(click.argument("waterflow", type=EnumType(WaterFlow))) def set_waterflow(self, waterflow: WaterFlow): """Set water flow setting.""" return self.send("set_water_box_custom_mode", [waterflow.value]) @classmethod def get_device_group(cls): @click.pass_context def callback(ctx, *args, id_file, **kwargs): gco = ctx.find_object(GlobalContextObject) if gco: kwargs["debug"] = gco.debug start_id = manual_seq = 0 try: with open(id_file, "r") as f: x = json.load(f) start_id = x.get("seq", 0) manual_seq = x.get("manual_seq", 0) _LOGGER.debug("Read stored sequence ids: %s", x) except (FileNotFoundError, TypeError, ValueError): pass ctx.obj = cls(*args, start_id=start_id, **kwargs) ctx.obj.manual_seqnum = manual_seq dg = DeviceGroup( cls, params=DeviceGroup.DEFAULT_PARAMS + [ click.Option( ["--id-file"], type=click.Path(dir_okay=False, writable=True), default=os.path.join(user_cache_dir("python-miio"), "python-mirobo.seq"), ) ], callback=callback, ) @dg.resultcallback() @dg.device_pass def cleanup(vac: Vacuum, *args, **kwargs): if vac.ip is None: # dummy Device for discovery, skip teardown return id_file = kwargs["id_file"] seqs = { "seq": vac._protocol.raw_id, "manual_seq": vac.manual_seqnum } _LOGGER.debug("Writing %s to %s", seqs, id_file) path_obj = pathlib.Path(id_file) cache_dir = path_obj.parents[0] cache_dir.mkdir(parents=True, exist_ok=True) with open(id_file, "w") as f: json.dump(seqs, f) return dg
class Heater(Device): """Main class representing the Smartmi Zhimi Heater.""" def __init__( self, ip: str = None, token: str = None, start_id: int = 0, debug: int = 0, lazy_discover: bool = True, model: str = MODEL_HEATER_ZA1, ) -> None: super().__init__(ip, token, start_id, debug, lazy_discover) if model in SUPPORTED_MODELS.keys(): self.model = model else: self.model = MODEL_HEATER_ZA1 @command(default_output=format_output( "", "Power: {result.power}\n" "Target temperature: {result.target_temperature} °C\n" "Temperature: {result.temperature} °C\n" "Humidity: {result.humidity} %\n" "Display brightness: {result.brightness}\n" "Buzzer: {result.buzzer}\n" "Child lock: {result.child_lock}\n" "Power-off time: {result.delay_off_countdown}\n", )) def status(self) -> HeaterStatus: """Retrieve properties.""" properties = SUPPORTED_MODELS[self.model]["available_properties"] # A single request is limited to 16 properties. Therefore the # properties are divided into multiple requests _props_per_request = 15 # The MA1, ZA1 is limited to a single property per request if self.model in [MODEL_HEATER_MA1, MODEL_HEATER_ZA1]: _props_per_request = 1 values = self.get_properties(properties, max_properties=_props_per_request) return HeaterStatus(dict(zip(properties, values))) @command(default_output=format_output("Powering on")) def on(self): """Power on.""" return self.send("set_power", ["on"]) @command(default_output=format_output("Powering off")) def off(self): """Power off.""" return self.send("set_power", ["off"]) @command( click.argument("temperature", type=int), default_output=format_output( "Setting target temperature to {temperature}"), ) def set_target_temperature(self, temperature: int): """Set target temperature.""" min_temp, max_temp = SUPPORTED_MODELS[self.model]["temperature_range"] if not min_temp <= temperature <= max_temp: raise HeaterException("Invalid target temperature: %s" % temperature) return self.send("set_target_temperature", [temperature]) @command( click.argument("brightness", type=EnumType(Brightness)), default_output=format_output( "Setting display brightness to {brightness}"), ) def set_brightness(self, brightness: Brightness): """Set display brightness.""" return self.send("set_brightness", [brightness.value]) @command( click.argument("buzzer", type=bool), default_output=format_output(lambda buzzer: "Turning on buzzer" if buzzer else "Turning off buzzer"), ) def set_buzzer(self, buzzer: bool): """Set buzzer on/off.""" if buzzer: return self.send("set_buzzer", ["on"]) else: return self.send("set_buzzer", ["off"]) @command( click.argument("lock", type=bool), default_output=format_output(lambda lock: "Turning on child lock" if lock else "Turning off child lock"), ) def set_child_lock(self, lock: bool): """Set child lock on/off.""" if lock: return self.send("set_child_lock", ["on"]) else: return self.send("set_child_lock", ["off"]) @command( click.argument("seconds", type=int), default_output=format_output( "Setting delayed turn off to {seconds} seconds"), ) def delay_off(self, seconds: int): """Set delay off seconds.""" min_delay, max_delay = SUPPORTED_MODELS[self.model]["delay_off_range"] if not min_delay <= seconds <= max_delay: raise HeaterException("Invalid delay time: %s" % seconds) if self.model == MODEL_HEATER_ZA1: return self.send("set_poweroff_time", [seconds]) elif self.model == MODEL_HEATER_MA1: return self.send("set_poweroff_level", [seconds // 3600]) return None
class AirDehumidifier(Device): """Implementation of Xiaomi Mi Air Dehumidifier.""" def __init__( self, ip: str = None, token: str = None, start_id: int = 0, debug: int = 0, lazy_discover: bool = True, model: str = MODEL_DEHUMIDIFIER_V1, ) -> None: super().__init__(ip, token, start_id, debug, lazy_discover) if model in AVAILABLE_PROPERTIES: self.model = model else: self.model = MODEL_DEHUMIDIFIER_V1 self.device_info = None @command(default_output=format_output( "", "Power: {result.power}\n" "Mode: {result.mode}\n" "Temperature: {result.temperature} °C\n" "Humidity: {result.humidity} %\n" "Buzzer: {result.buzzer}\n" "LED : {result.led}\n" "Child lock: {result.child_lock}\n" "Target humidity: {result.target_humidity} %\n" "Fan speed: {result.fan_speed}\n" "Tank Full: {result.tank_full}\n" "Compressor Status: {result.compressor_status}\n" "Defrost Status: {result.defrost_status}\n" "Fan st: {result.fan_st}\n" "Alarm: {result.alarm}\n", )) def status(self) -> AirDehumidifierStatus: """Retrieve properties.""" if self.device_info is None: self.device_info = self.info() properties = AVAILABLE_PROPERTIES[self.model] values = self.get_properties(properties, max_properties=1) return AirDehumidifierStatus( defaultdict(lambda: None, zip(properties, values)), self.device_info) @command(default_output=format_output("Powering on")) def on(self): """Power on.""" return self.send("set_power", ["on"]) @command(default_output=format_output("Powering off")) def off(self): """Power off.""" return self.send("set_power", ["off"]) @command( click.argument("mode", type=EnumType(OperationMode, False)), default_output=format_output("Setting mode to '{mode.value}'"), ) def set_mode(self, mode: OperationMode): """Set mode.""" try: return self.send("set_mode", [mode.value]) except DeviceError as error: # {'code': -6011, 'message': 'device_poweroff'} if error.code == -6011: self.on() return self.send("set_mode", [mode.value]) raise @command( click.argument("fan_speed", type=EnumType(FanSpeed, False)), default_output=format_output("Setting fan level to {fan_level}"), ) def set_fan_speed(self, fan_speed: FanSpeed): """Set the fan speed.""" return self.send("set_fan_level", [fan_speed.value]) @command( click.argument("led", type=bool), default_output=format_output(lambda led: "Turning on LED" if led else "Turning off LED"), ) def set_led(self, led: bool): """Turn led on/off.""" if led: return self.send("set_led", ["on"]) else: return self.send("set_led", ["off"]) @command( click.argument("buzzer", type=bool), default_output=format_output(lambda buzzer: "Turning on buzzer" if buzzer else "Turning off buzzer"), ) def set_buzzer(self, buzzer: bool): """Set buzzer on/off.""" if buzzer: return self.send("set_buzzer", ["on"]) else: return self.send("set_buzzer", ["off"]) @command( click.argument("lock", type=bool), default_output=format_output(lambda lock: "Turning on child lock" if lock else "Turning off child lock"), ) def set_child_lock(self, lock: bool): """Set child lock on/off.""" if lock: return self.send("set_child_lock", ["on"]) else: return self.send("set_child_lock", ["off"]) @command( click.argument("humidity", type=int), default_output=format_output("Setting target humidity to {humidity}"), ) def set_target_humidity(self, humidity: int): """Set the auto target humidity.""" if humidity not in [40, 50, 60]: raise AirDehumidifierException("Invalid auto target humidity: %s" % humidity) return self.send("set_auto", [humidity])
) HOSTNAME_PROMPT = click.option( "--host", "host", is_eager=False, prompt=True, callback=trim_trailing_slash, envvar=("C8Y_HOST", "C8Y_BASEURL", "C8Y_URL"), help="Cumulocity Hostname [required] [env var: C8Y_HOST]", ) ARG_DEVICE = click.argument( "device", nargs=1, required=True, ) DEVICE = click.option( "--device", "-d", required=False, envvar="C8YLP_DEVICE", show_envvar=True, help="Device external identity", ) EXTERNAL_IDENTITY_TYPE = click.option( "--external-type", envvar="C8YLP_EXTERNAL_TYPE",
if retval is None and value is not None: try: value = value.strip(', []') retval = tuple(float(x) for x in re.split(r'[,\s]+', value)) assert len(retval) == 4 return retval except Exception: raise click.BadParameter( "{0!r} is not a valid bounding box representation".format( value)) else: # pragma: no cover return retval # Singular input file file_in_arg = click.argument('INPUT', callback=file_in_handler) # Singular output file file_out_arg = click.argument('OUTPUT', type=click.Path(resolve_path=True)) # Multiple input files. files_in_arg = click.argument('files', nargs=-1, type=click.Path(), required=True, metavar="INPUTS...", callback=files_in_handler) # Multiple files, last of which is an output file. files_inout_arg = click.argument('files', nargs=-1,
class Cooker(Device): """Main class representing the cooker.""" @command(default_output=format_output( "", "Mode: {result.mode}\n" "Menu: {result.menu}\n" "Stage: {result.stage}\n" "Temperature: {result.temperature}\n" "Start time: {result.start_time}\n" "Remaining: {result.remaining}\n" "Cooking delayed: {result.cooking_delayed}\n" "Duration: {result.duration}\n" "Settings: {result.settings}\n" "Interaction timeouts: {result.interaction_timeouts}\n" "Hardware version: {result.hardware_version}\n" "Firmware version: {result.firmware_version}\n" "Favorite: {result.favorite}\n" "Custom: {result.custom}\n", )) def status(self) -> CookerStatus: """Retrieve properties.""" properties = [ "func", "menu", "stage", "temp", "t_func", "t_precook", "t_cook", "setting", "delay", "version", "favorite", "custom", ] """ Some cookers doesn't support a list of properties here. Therefore "all" properties are requested. If the property count or order changes the property list above must be updated. """ values = self.send("get_prop", ["all"]) properties_count = len(properties) values_count = len(values) if properties_count != values_count: _LOGGER.debug( "Count (%s) of requested properties does not match the " "count (%s) of received values.", properties_count, values_count, ) return CookerStatus(defaultdict(lambda: None, zip(properties, values))) @command( click.argument("profile", type=str), default_output=format_output("Cooking profile started"), ) def start(self, profile: str): """Start cooking a profile.""" if not self._validate_profile(profile): raise CookerException("Invalid cooking profile: %s" % profile) self.send("set_start", [profile]) @command(default_output=format_output("Cooking stopped")) def stop(self): """Stop cooking.""" self.send("set_func", ["end02"]) @command(default_output=format_output("Cooking stopped")) def stop_outdated_firmware(self): """Stop cooking (obsolete).""" self.send("set_func", ["end"]) @command(default_output=format_output("Setting no warnings")) def set_no_warnings(self): """Disable warnings.""" self.send("set_func", ["nowarn"]) @command(default_output=format_output("Setting acknowledge")) def set_acknowledge(self): """Enable warnings?""" self.send("set_func", ["ack"]) # FIXME: Add unified CLI support def set_interaction(self, settings: CookerSettings, timeouts: InteractionTimeouts): """Set interaction. Supported by all cookers except MODEL_PRESS1""" self.send( "set_interaction", [ str(settings), "{:x}".format(timeouts.led_off), "{:x}".format(timeouts.lid_open), "{:x}".format(timeouts.lid_open_warning), ], ) @command( click.argument("profile", type=str), default_output=format_output("Setting menu to {profile}"), ) def set_menu(self, profile: str): """Select one of the default(?) cooking profiles""" if not self._validate_profile(profile): raise CookerException("Invalid cooking profile: %s" % profile) self.send("set_menu", [profile]) @command(default_output=format_output("", "Temperature history: {result}\n")) def get_temperature_history(self) -> TemperatureHistory: """Retrieves a temperature history. The temperature is only available while cooking. Approx. six data points per minute. """ data = self.send("get_temp_history") return TemperatureHistory(data[0]) @staticmethod def _validate_profile(profile): return all(c in string.hexdigits for c in profile) and len(profile) in [ 228, 242, ]
help='Alternative odd config file', type=click.Path(exists=False), default=os.path.expanduser(PIUCONFIG)) option_pg_service_file = click.option('--pg_service-file', help='The PostgreSQL service file', envvar='PGSERVICEFILE', type=click.Path(exists=True)) option_region = click.option('--region', envvar='AWS_DEFAULT_REGION', metavar='AWS_REGION_ID', help='AWS region ID (e.g. eu-west-1)') option_reuse = click.option('--reuse/--no-reuse', default=True, help='Reuse an already exisiting tunnel') cluster_argument = click.argument('cluster') class Spilo( collections.namedtuple( 'Spilo', 'stack_name, version, dns, elb, instances, vpc_id, stack')): pass @click.group(cls=AliasedGroup) def cli(): """ Spilo can connect to your Spilo cluster running inside a vpc. It does this using the stups infrastructure. """