def cancel_skills(misty: Misty) -> Dict: """Cancels all skills currently running on Misty. Args: misty (Misty): The Misty on which to cancel running skills. Returns: Dict: a dictionary with the key `overall_success` specifying whether all actions were successful and a key for every action containing the dictionarised Misty2pyResponse. """ actions = ActionLog() data = misty.get_info("skills_running").parse_to_dict() actions.append_({"get_running_skills": data}) data = data.get("rest_response", {}) result = data.get("result", []) to_cancel = [] for dct in result: uid = dct.get("uniqueId", "") if len(uid) > 0: to_cancel.append(uid) for skill in to_cancel: data = misty.perform_action("skill_cancel", data={ "Skill": skill }).parse_to_dict() actions.append_({"cancel_%s" % skill: data}) return success_of_action_list(actions.get_())
def drive_left(self, misty: Misty, velocity: int, angle: int): """Drives left with the specified velocity and angle.""" left = { "LinearVelocity": self._parse_velocity(velocity), "AngularVelocity": self._parse_angle(angle), } return misty.perform_action("drive", data=left).parse_to_dict()
def get_non_system_assets( misty: Misty, assets: List = ["audio", "image", "video", "recording"], ) -> Dict: """Obtains the list of all non-system assets that fall under one of the `assets` types. Args: misty (Misty): The Misty whose non system assets to obtain. assets (List, optional): The types of assets to obtain. Defaults to `["audio", "image", "video", "recording"]`. Returns: Dict: all non-system assets of a given type (keywords) listed as their names in the list under the type keyword. """ action = "_list" non_sys_assets = {} for asset in assets: data = misty.get_info(asset + action).parse_to_dict() data = data.get("rest_response", {}) result = data.get("result", []) for hit in result: name = hit.get("name") if not name: continue is_system = hit.get("systemAsset", True) if not is_system: if not non_sys_assets.get(asset): non_sys_assets[asset] = [] non_sys_assets[asset].append(name) print("Found a non-system asset of type `%s` named `%s`." % (asset, name)) return non_sys_assets
def delete_assets(misty: Misty, assets: Dict, ignore_list: List = []) -> List[str]: """Deletes all non-system files except those that are in `ignore_list`. Args: misty (Misty): the Misty whose assets to delete. assets (Dict): The dictionary of assets; a list of file names under their asset type as the keyword. ignore_list (List, optional): The list of file names for files that should not be deleted. Defaults to `[]`. Returns: List[str]: The list of files that were succesfully deleted. """ action = "_delete" delete_list = [] for asset_type, files in assets.items(): for file in files: if file not in ignore_list: if asset_type == "recording": data = {"Name": file} else: data = {"FileName": file} response = misty.perform_action(asset_type + action, data=data).parse_to_dict() response = response.get("rest_response", {}) status = response.get("success") if status: print("Successfully deleted the asset `%s`." % file) delete_list.append(file) else: print("Failed to delete the asset `%s`. Message: `%s`" % (file, response)) return delete_list
def drive_backward(self, misty: Misty, velocity: int): """Drives backwards with the specified velocity.""" back = { "LinearVelocity": self._parse_velocity(velocity, negative=True), "AngularVelocity": self.stagnant_angle, } return misty.perform_action("drive", data=back).parse_to_dict()
def drive_forward(self, misty: Misty, velocity: int) -> Dict: """Drives forwards with the specified velocity.""" forw = { "LinearVelocity": self._parse_velocity(velocity), "AngularVelocity": self.stagnant_angle, } return misty.perform_action("drive", data=forw).parse_to_dict()
def get_misty(env_path: str = ".env") -> Callable: """Obtains a Misty instance using the `MISTY_IP_ADDRESS` in the supplied .env file.""" from misty2py.robot import Misty from misty2py.utils.env_loader import EnvLoader env_loader = EnvLoader(get_abs_path(env_path)) return Misty(env_loader.get_ip())
def drive_right(self, misty: Misty, velocity: int, angle: int): """Drives right with the specified velocity and angle.""" velocity = self._parse_velocity(velocity) right = { "LinearVelocity": self._parse_velocity(velocity), "AngularVelocity": self._parse_angle(angle, negative=True), } return misty.perform_action("drive", data=right).parse_to_dict()
def speak(misty: Misty, utterance: str) -> Dict: """Speaks an utterance and returns descriptive message. Args: misty (Misty): The Misty that speaks the utterance. utterance (str): The utterance to speak. Returns: Dict: the enhanced dictionarised Misty2pyResponse. """ result = misty.perform_action( "speak", data={"Text": utterance, "UtteranceId": "utterance_" + get_random_string(6)}, ).parse_to_dict() return compose_custom_response( result, success_message="Talking successful. Utterance: `%s`." % utterance, fail_message="Talking failed. Utterance: `%s`." % utterance, )
def save_assets(misty: Misty, assets: Dict, location: str, overwrite: bool) -> List[str]: """Saves multiple assets. Args: misty (Misty): the Misty whose assets to save. assets (Dict): The dictionary of assets; a list of file names under their asset type as the keyword. location (str): The absolute path to the directory where the assets are saved. overwrite (bool): Whether the overwrite the file if it exists. Returns: List[str]: A list of file names for the files that failed to be saved. """ failed_list = [] action = "_file" for asset_type, files in assets.items(): for file in files: params, name, ext = get_asset_properties(asset_type, file) response = misty.get_info(asset_type + action, params=params).parse_to_dict() response = response.get("rest_response", {}) result = response.get("result") if not result: failed_list.append(file) else: file_name = "%s_%s_%s_in_base64.txt" % (asset_type, name, ext) full_path = os.path.join(location, file_name) file_content = result.get("base64") if not file_content: failed_list.append(file) else: success = save_base64_str(full_path, file_content, overwrite) if not success: failed_list.append(file) return failed_list
def test_get_misty(): from misty2py.robot import Misty m1 = get_misty() m2 = Misty(values.get("MISTY_IP_ADDRESS")) assert print(m1) == print(m2)
import pytest import time from misty2py.basic_skills import ( cancel_skills, expression, free_memory, movement, speak, ) from misty2py.robot import Misty from misty2py.utils.env_loader import EnvLoader env_loader = EnvLoader() MISTY = Misty(env_loader.get_ip()) def test_cancel_skills(): result = cancel_skills.cancel_skills(MISTY) assert result.get("overall_success") def test_free_memory(): result = free_memory.free_memory(MISTY, overwrite=True) assert result.get("overall_success") def test_speak(): phrase = "Hello!" result = speak.speak(MISTY, phrase)
DIR_NAME = path.abspath(path.dirname(__file__)) with open(path.join(DIR_NAME, "custom_allowed_actions.json")) as f: custom_allowed_infos = json.loads(f.read()) with open(path.join(DIR_NAME, "custom_allowed_data.json")) as f: custom_allowed_data = json.loads(f.read()) with open(path.join(DIR_NAME, "custom_allowed_infos.json")) as f: custom_allowed_actions = json.loads(f.read()) env_loader = EnvLoader() MISTY = Misty( env_loader.get_ip(), custom_info=custom_allowed_infos, custom_actions=custom_allowed_actions, custom_data=custom_allowed_data, ) def test_env_loader(): env = EnvLoader(".env.example") assert env.get_ip() == "0.0.0.0" def test_misty_print(): assert str(MISTY) == "A Misty II robot with IP address %s" % env_loader.get_ip() def test_basic_action_dict_method(): colours = {"red": "200", "green": "20", "blue": "40"}
def free_memory( misty: Misty, assets: List = ["audio", "image", "video", "recording"], save: bool = True, overwrite: bool = False, save_dir: str = "data", ) -> Dict: """Removes all non-system files in Misty's memory. Args: misty (Misty): the Misty whose memory to free. assets (List, optional): The list of asset types to delete. Defaults to `["audio", "image", "video", "recording"]`. save (bool, optional): Whether to save the files that are being deleted from Misty's memory. Defaults to `True`. overwrite (bool): Whether the overwrite the file if it exists. Defaults to `False`. save_dir (str, optional): The path to the directory that will store saved assets. Defaults to `"data"`. Returns: Dict: a dictionary with the key `overall_success` specifying whether all actions were successful and a key for every action containing the dictionarised Misty2pyResponse to that action. """ save_dir = get_abs_path(save_dir) enable_audio = misty.perform_action("audio_enable").parse_to_dict() enable_av = misty.perform_action("streaming_av_enable").parse_to_dict() enable_camera = misty.perform_action("camera_enable").parse_to_dict() assets_to_delete = get_non_system_assets(misty, assets=assets) deletion = {} if len(assets_to_delete) == 0: deletion["success"] = True deletion["message"] = "No non-system files present." else: failed_to_save_list = [] if save: failed_to_save_list = save_assets(misty, assets_to_delete, save_dir, overwrite) deleted = delete_assets(misty, assets_to_delete, failed_to_save_list) if len(deleted) > 0: deletion["success"] = True deletion[ "message"] = "Successfully deleted following assets: %s" % str( deleted) else: deletion["success"] = False deletion["message"] = "Failed to delete any assets." disable_audio = misty.perform_action("audio_disable").parse_to_dict() disable_av = misty.perform_action("streaming_av_disable").parse_to_dict() disable_camera = misty.perform_action("camera_disable").parse_to_dict() return success_of_action_dict( enable_audio=enable_audio, enable_av=enable_av, enable_camera=enable_camera, deletion={ "overall_success": deletion["success"], "misty2py_response": deletion, }, disable_audio=disable_audio, disable_av=disable_av, disable_camera=disable_camera, )
def stop_driving(self, misty: Misty): """Stops driving.""" return misty.perform_action("drive_stop").parse_to_dict()
def expression( misty: Misty, image: Union[str, Dict, None] = None, sound: Union[str, Dict, None] = None, colour_type: Union[str, None] = None, colour: Union[str, Dict, None] = None, colours: Union[str, Dict, None] = None, image_offset: Union[float, int] = 0, sound_offset: Union[float, int] = 0, colour_offset: Union[float, int] = 0, duration: Union[float, int] = 1.5, ) -> Dict: """Performs an audio-visual expression. Args: misty (Misty): the Misty that performs the expression. image (Union[str, Dict, None], optional): The path to the image to display or `None` if no special image should be displayed. Defaults to `None`. sound (Union[str, Dict, None], optional): The path to the sound to play or None if no sound should be played. Defaults to `None`. colour_type (Union[str, None], optional): `"trans"` if a colour transition should be lit. Defaults to `None`. colour (Union[str, Dict, None], optional): The colour of LED as a dictionary or a data shortcut or `None` if no special colour should be lit. Defaults to `None`. colours (Union[str, Dict, None], optional): The LED colours as a dictionary or a data shortcut or `None` if no transitioning colours should be lit. Defaults to `None`. image_offset (Union[float, int], optional): The offset between starting the expression and displaying the image in ms. Defaults to `0`. sound_offset (Union[float, int], optional): The offset between starting the expression and playing the sound in ms. Defaults to `0`. colour_offset (Union[float, int], optional): The offset between starting the expression and lighting the LEDs in ms. Defaults to `0`. duration (Union[float, int], optional): The duration of the expression. Defaults to `1.5`. Returns: Dict: a dictionary with the key `overall_success` specifying whether all actions were successful and a key for every action containing the dictionarised Misty2pyResponse for that action. """ assert duration > 0, "Duration must be higher than zero." assert (colour_offset >= 0 and sound_offset >= 0 and image_offset >= 0), "Offsets must be higher or equal to zero" assert (image or sound or colour or colours), "At least one audio-visual component \ (display image, sound or led colour / colours) must be set." assert (image_offset < duration and sound_offset < duration and colour_offset < duration ), "The offsets cannot be higher than the duration." actions = ActionLog() reset_img = False reset_led = False offsets = sorted(set([image_offset, sound_offset, colour_offset])) for i, offset in enumerate(offsets): time.sleep(offset) if offset == image_offset: if image: img_show_message = misty.perform_action( "image_show", data=image).parse_to_dict() actions.append_({"img_show": img_show_message}) reset_img = True if offset == sound_offset: if sound: audio_play_message = misty.perform_action( "audio_play", data=sound).parse_to_dict() actions.append_({"audio_play": audio_play_message}) if offset == colour_offset: if colour_type == "trans": json_cols = None if colours: json_cols = construct_transition_dict( colours, misty.actions.allowed_data) elif colour: json_cols = construct_transition_dict( { "col1": colour, "col2": "led_off" }, misty.actions.allowed_data) if json_cols: led_trans_message = misty.perform_action( "led_trans", data=json_cols).parse_to_dict() actions.append_({"led_trans": led_trans_message}) reset_led = True else: if colour: json_col = get_rgb_from_unknown( colour, allowed_data=misty.actions.allowed_data) led_message = misty.perform_action( "led", data=json_col).parse_to_dict() actions.append_({"led": led_message}) reset_led = True if i == len(offsets) - 1: time.sleep(duration - offset) if reset_img: reset_img_message = misty.perform_action( "image_show", data="image_content_default").parse_to_dict() actions.append_({"reset_img": reset_img_message}) if reset_led: reset_led_message = misty.perform_action( "led", data="led_off").parse_to_dict() actions.append_({"reset_led": reset_led_message}) return success_of_action_list(actions.get_())