def news(news_source: str = 'fox') -> None: """Says news around the user's location. Args: news_source: Source from where the news has to be fetched. Defaults to ``fox``. """ if not env.news_api: logger.warning("News apikey not found.") support.no_env_vars() return sys.stdout.write(f'\rGetting news from {news_source} news.') news_client = NewsApiClient(api_key=env.news_api) try: all_articles = news_client.get_top_headlines( sources=f'{news_source}-news') except newsapi_exception.NewsAPIException: speaker.speak( text=f"I wasn't able to get the news {env.title}! " "I think the News API broke, you may try after sometime.") return speaker.speak(text="News around you!") speaker.speak(text=' '.join( [article['title'] for article in all_articles['articles']])) if shared.called_by_offline: return if shared.called['report'] or shared.called['time_travel']: speaker.speak(run=True)
def locate(phrase: str) -> None: """Locates an Apple device using icloud api for python. Args: phrase: Takes the voice recognized statement as argument and extracts device name from it. """ if not (target_device := device_selector(phrase=phrase)): support.no_env_vars() return
def robinhood() -> None: """Gets investment details from robinhood API.""" if not all([env.robinhood_user, env.robinhood_pass, env.robinhood_qr]): logger.warning("Robinhood username, password or QR code not found.") support.no_env_vars() return sys.stdout.write("\rGetting your investment details.") rh = Robinhood() rh.login(username=env.robinhood_user, password=env.robinhood_pass, qr_code=env.robinhood_qr) raw_result = rh.positions() result = raw_result["results"] stock_value = watcher(rh, result) speaker.speak(text=stock_value)
def github(phrase: str) -> None: """Pre-process to check the phrase received and call the ``GitHub`` function as necessary. Args: phrase: Takes the phrase spoken as an argument. """ if 'update yourself' in phrase or 'update your self' in phrase: update() return if not all([env.git_user, env.git_pass]): logger.warning("Github username or token not found.") support.no_env_vars() return auth = HTTPBasicAuth(env.git_user, env.git_pass) response = requests.get( 'https://api.github.com/user/repos?type=all&per_page=100', auth=auth).json() result, repos, total, forked, private, archived, licensed = [], [], 0, 0, 0, 0, 0 for i in range(len(response)): total += 1 forked += 1 if response[i]['fork'] else 0 private += 1 if response[i]['private'] else 0 archived += 1 if response[i]['archived'] else 0 licensed += 1 if response[i]['license'] else 0 repos.append({ response[i]['name'].replace('_', ' ').replace('-', ' '): response[i]['clone_url'] }) if 'how many' in phrase: speaker.speak( text= f'You have {total} repositories {env.title}, out of which {forked} are forked, {private} are private, ' f'{licensed} are licensed, and {archived} archived.') elif not shared.called_by_offline: [ result.append(clone_url) if clone_url not in result and re.search(rf'\b{word}\b', repo.lower()) else None for word in phrase.lower().split() for item in repos for repo, clone_url in item.items() ] if result: github_controller(target=result) else: speaker.speak(text=f"Sorry {env.title}! I did not find that repo.")
def car(phrase: str) -> None: """Controls the car to lock, unlock or remote start. Args: phrase: Takes the phrase spoken as an argument. See Also: API climate controls: 31 is LO, 57 is HOT Car Climate controls: 58 is LO, 84 is HOT """ if not all([env.car_email, env.car_pass, env.car_pin]): logger.warning("InControl email or password or PIN not found.") support.no_env_vars() return disconnected = f"I wasn't able to connect your car {env.title}! Please check the logs for more information." if "start" in phrase or "set" in phrase or "turn on" in phrase: if not shared.called_by_offline: playsound(sound=indicators.exhaust, block=False) extras = "" if target_temp := support.extract_nos(input_=phrase, method=int): if target_temp < 57: target_temp = 57 elif target_temp > 83: target_temp = 83 elif "high" in phrase or "highest" in phrase: target_temp = 83 elif "low" in phrase or "lowest" in phrase: target_temp = 57 else: if vehicle_position := vehicle(operation="LOCATE_INTERNAL"): current_temp, target_temp = get_current_temp( location=vehicle_position) extras += f"Your car is in {vehicle_position['city']} {vehicle_position['state']}, where the " \ f"current temperature is {current_temp}, so " else:
def lights(phrase: str) -> Union[None, NoReturn]: """Controller for smart lights. Args: phrase: Takes the voice recognized statement as argument. """ if not vpn_checker(): return if not os.path.isfile(fileio.smart_devices): logger.warning(f"{fileio.smart_devices} not found.") support.no_env_vars() return try: with open(fileio.smart_devices) as file: smart_devices = yaml.load(stream=file, Loader=yaml.FullLoader) or {} except yaml.YAMLError as error: logger.error(error) speaker.speak( text= f"I'm sorry {env.title}! I wasn't able to read the source information. " "Please check the logs.") return if not any(smart_devices): logger.warning(f"{fileio.smart_devices} is empty.") support.no_env_vars() return phrase = phrase.lower() def turn_off(host: str) -> NoReturn: """Turns off the device. Args: host: Takes target device IP address as an argument. """ smart_lights.MagicHomeApi(device_ip=host, device_type=1, operation='Turn Off').turn_off() def warm(host: str) -> NoReturn: """Sets lights to warm/yellow. Args: host: Takes target device IP address as an argument. """ smart_lights.MagicHomeApi(device_ip=host, device_type=1, operation='Warm Lights').update_device( r=0, g=0, b=0, warm_white=255) def cool(host: str) -> NoReturn: """Sets lights to cool/white. Args: host: Takes target device IP address as an argument. """ smart_lights.MagicHomeApi(device_ip=host, device_type=2, operation='Cool Lights').update_device( r=255, g=255, b=255, warm_white=255, cool_white=255) def preset(host: str, value: int) -> NoReturn: """Changes light colors to preset values. Args: host: Takes target device IP address as an argument. value: Preset value extracted from list of verified values. """ smart_lights.MagicHomeApi( device_ip=host, device_type=2, operation='Preset Values').send_preset_function( preset_number=value, speed=101) def lumen(host: str, rgb: int = 255) -> NoReturn: """Sets lights to custom brightness. Args: host: Takes target device IP address as an argument. rgb: Red, Green andBlue values to alter the brightness. """ args = {'r': 255, 'g': 255, 'b': 255, 'warm_white': rgb} smart_lights.MagicHomeApi( device_ip=host, device_type=1, operation='Custom Brightness').update_device(**args) if 'all' in phrase.split(): # Checking for list since lights are inserted as a list and tv as string host_names = [ value for key, value in smart_devices.items() if isinstance(value, list) ] light_location = "" else: # Get the closest matching name provided in smart_devices.yaml compared to what's requested by the user light_location = support.get_closest_match(text=phrase, match_list=list( smart_devices.keys())) host_names = [smart_devices.get(light_location)] light_location = light_location.replace('_', ' ').replace('-', '') host_names = support.matrix_to_flat_list(input_=host_names) host_names = list(filter(None, host_names)) if light_location and not host_names: logger.warning( f"No hostname values found for {light_location} in {fileio.smart_devices}" ) speaker.speak( text= f"I'm sorry {env.title}! You haven't mentioned the host names of '{light_location}' lights." ) return if not host_names: Thread(target=support.unrecognized_dumper, args=[{ 'LIGHTS': phrase }]).start() speaker.speak(text=f"I'm not sure which lights you meant {env.title}!") return host_ip = [ support.hostname_to_ip(hostname=hostname) for hostname in host_names ] # host_ip = list(filter(None, host_ip)) host_ip = support.matrix_to_flat_list(input_=host_ip) if not host_ip: plural = 'lights' if len(host_ip) > 1 else 'light' speaker.speak( text= f"I wasn't able to connect to your {light_location} {plural} {env.title}! " f"{support.number_to_words(input_=len(host_ip), capitalize=True)} lights appear to be " "powered off.") return def avail_check(function_to_call: Callable) -> NoReturn: """Speaks an error message if any of the lights aren't reachable. Args: function_to_call: Takes the function/method that has to be called as an argument. """ status = ThreadPool(processes=1).apply_async(func=thread_worker, args=[function_to_call]) speaker.speak(run=True) if failed := status.get(timeout=5): plural_ = "lights aren't available right now!" if failed > 1 else "light isn't available right now!" speaker.speak( text= f"I'm sorry sir! {support.number_to_words(input_=failed, capitalize=True)} {plural_}" )
def television(phrase: str) -> None: """Controls all actions on a TV (LG Web OS). Args: phrase: Takes the voice recognized statement as argument. """ if not vpn_checker(): return if not os.path.isfile(fileio.smart_devices): logger.warning(f"{fileio.smart_devices} not found.") support.no_env_vars() return try: with open(fileio.smart_devices) as file: smart_devices = yaml.load(stream=file, Loader=yaml.FullLoader) or {} except yaml.YAMLError as error: logger.error(error) speaker.speak( text= f"I'm sorry {env.title}! I was unable to read your TV's source information." ) return if not env.tv_mac or not env.tv_client_key: logger.warning("IP, MacAddress [or] ClientKey not found.") support.no_env_vars() return tv_ip_list = support.hostname_to_ip( hostname=smart_devices.get('tv', 'LGWEBOSTV')) tv_ip_list = list(filter(None, tv_ip_list)) if not tv_ip_list: speaker.speak( text= f"I'm sorry {env.title}! I wasn't able to get the IP address of your TV." ) return phrase_exc = phrase.replace('TV', '') phrase_lower = phrase_exc.lower() def tv_status(attempt: int = 0) -> str: """Pings the tv and returns the status. 0 if able to ping, 256 if unable to ping. Args: attempt: Takes iteration count as an argument. Returns: int: Returns the reachable IP address from the list. """ for ip in tv_ip_list: if env.macos: if tv_stat := os.system( f"ping -c 1 -t 2 {ip} >/dev/null 2>&1"): logger.error( f"Connection timed out on {ip}. Ping result: {tv_stat}" ) if not attempt else None else: return ip else: if tv_stat := os.system(f"ping -c 1 -t 2 {ip} > NUL"): logger.error( f"Connection timed out on {ip}. Ping result: {tv_stat}" ) if not attempt else None else:
def weather(phrase: str = None) -> None: """Says weather at any location if a specific location is mentioned. Says weather at current location by getting IP using reverse geocoding if no place is received. Args: phrase: Takes the phrase spoken as an argument. """ if not env.weather_api: logger.warning("Weather apikey not found.") support.no_env_vars() return place = None if phrase: place = support.get_capitalized(phrase=phrase) phrase = phrase.lower() sys.stdout.write('\rGetting your weather info') if place: desired_location = geo_locator.geocode(place) coordinates = desired_location.latitude, desired_location.longitude located = geo_locator.reverse(coordinates, language='en') address = located.raw['address'] city = address.get('city') state = address.get('state') lat = located.latitude lon = located.longitude else: try: with open(fileio.location) as file: current_location = yaml.load(stream=file, Loader=yaml.FullLoader) except yaml.YAMLError as error: logger.error(error) speaker.speak( text= f"I'm sorry {env.title}! I wasn't able to read your location.") return address = current_location.get('address', {}) city = address.get('city', address.get('hamlet', 'Unknown')) state = address.get('state', 'Unknown') lat = current_location['latitude'] lon = current_location['longitude'] weather_url = f'https://api.openweathermap.org/data/2.5/onecall?lat={lat}&lon={lon}&exclude=minutely,' \ f'hourly&appid={env.weather_api}' try: response = json.loads(urllib.request.urlopen( url=weather_url).read()) # loads the response in a json except (urllib.error.HTTPError, urllib.error.URLError) as error: logger.error(error) speaker.speak( text= f"I'm sorry {env.title}! I ran into an exception. Please check your logs." ) return weather_location = f'{city} {state}'.replace( 'None', '') if city != state else city or state if phrase and any(match_word in phrase for match_word in [ 'tomorrow', 'day after', 'next week', 'tonight', 'afternoon', 'evening' ]): if 'tonight' in phrase: key = 0 tell = 'tonight' elif 'day after' in phrase: key = 2 tell = 'day after tomorrow ' elif 'tomorrow' in phrase: key = 1 tell = 'tomorrow ' elif 'next week' in phrase: key = -1 next_week = datetime.fromtimestamp( response['daily'][-1]['dt']).strftime("%A, %B %d") tell = f"on {' '.join(next_week.split()[0:-1])} {engine().ordinal(next_week.split()[-1])}" else: key = 0 tell = 'today ' if 'morning' in phrase: when = 'morn' tell += 'morning' elif 'evening' in phrase: when = 'eve' tell += 'evening' elif 'tonight' in phrase: when = 'night' elif 'night' in phrase: when = 'night' tell += 'night' else: when = 'day' tell += '' if 'alerts' in response: alerts = response['alerts'][0]['event'] start_alert = datetime.fromtimestamp( response['alerts'][0]['start']).strftime("%I:%M %p") end_alert = datetime.fromtimestamp( response['alerts'][0]['end']).strftime("%I:%M %p") else: alerts, start_alert, end_alert = None, None, None during_key = response['daily'][key] condition = during_key['weather'][0]['description'] high = int(temperature.k2f(during_key['temp']['max'])) low = int(temperature.k2f(during_key['temp']['min'])) temp_f = int(temperature.k2f(during_key['temp'][when])) temp_feel_f = int(temperature.k2f(during_key['feels_like'][when])) sunrise = datetime.fromtimestamp( during_key['sunrise']).strftime("%I:%M %p") sunset = datetime.fromtimestamp( during_key['sunset']).strftime("%I:%M %p") if 'sunrise' in phrase or 'sun rise' in phrase or ('sun' in phrase and 'rise' in phrase): if datetime.strptime(datetime.today().strftime("%I:%M %p"), "%I:%M %p") >= \ datetime.strptime(sunrise, "%I:%M %p"): tense = "will be" else: tense = "was" speaker.speak( text= f"{tell} in {weather_location}, sunrise {tense} at {sunrise}.") return if 'sunset' in phrase or 'sun set' in phrase or ('sun' in phrase and 'set' in phrase): if datetime.strptime(datetime.today().strftime("%I:%M %p"), "%I:%M %p") >= \ datetime.strptime(sunset, "%I:%M %p"): tense = "will be" else: tense = "was" speaker.speak( text=f"{tell} in {weather_location}, sunset {tense} at {sunset}" ) return output = f"The weather in {weather_location} {tell} would be {temp_f}\N{DEGREE SIGN}F, with a high of " \ f"{high}, and a low of {low}. " if temp_feel_f != temp_f: output += f"But due to {condition} it will fee like it is {temp_feel_f}\N{DEGREE SIGN}F. " output += f"Sunrise at {sunrise}. Sunset at {sunset}. " if alerts and start_alert and end_alert: output += f'There is a weather alert for {alerts} between {start_alert} and {end_alert}' speaker.speak(text=output) return condition = response['current']['weather'][0]['description'] high = int(temperature.k2f(arg=response['daily'][0]['temp']['max'])) low = int(temperature.k2f(arg=response['daily'][0]['temp']['min'])) temp_f = int(temperature.k2f(arg=response['current']['temp'])) temp_feel_f = int(temperature.k2f(arg=response['current']['feels_like'])) sunrise = datetime.fromtimestamp( response['daily'][0]['sunrise']).strftime("%I:%M %p") sunset = datetime.fromtimestamp( response['daily'][0]['sunset']).strftime("%I:%M %p") if phrase: if 'sunrise' in phrase or 'sun rise' in phrase or ('sun' in phrase and 'rise' in phrase): if datetime.strptime(datetime.today().strftime("%I:%M %p"), "%I:%M %p") >= \ datetime.strptime(sunrise, "%I:%M %p"): tense = "will be" else: tense = "was" speaker.speak( text=f"In {weather_location}, sunrise {tense} at {sunrise}.") return if 'sunset' in phrase or 'sun set' in phrase or ('sun' in phrase and 'set' in phrase): if datetime.strptime(datetime.today().strftime("%I:%M %p"), "%I:%M %p") >= \ datetime.strptime(sunset, "%I:%M %p"): tense = "will be" else: tense = "was" speaker.speak( text=f"In {weather_location}, sunset {tense} at {sunset}") return if shared.called['time_travel']: if 'rain' in condition or 'showers' in condition: feeling = 'rainy' weather_suggest = 'You might need an umbrella" if you plan to head out.' elif temp_feel_f <= 40: feeling = 'freezing' weather_suggest = 'Perhaps" it is time for winter clothing.' elif 41 <= temp_feel_f <= 60: feeling = 'cool' weather_suggest = 'I think a lighter jacket would suffice" if you plan to head out.' elif 61 <= temp_feel_f <= 75: feeling = 'optimal' weather_suggest = 'It might be a perfect weather for a hike, or perhaps a walk.' elif 76 <= temp_feel_f <= 85: feeling = 'warm' weather_suggest = 'It is a perfect weather for some outdoor entertainment.' elif temp_feel_f > 85: feeling = 'hot' weather_suggest = "I would not recommend thick clothes today." else: feeling, weather_suggest = '', '' wind_speed = response['current']['wind_speed'] if wind_speed > 10: output = f'The weather in {city} is {feeling} {temp_f}\N{DEGREE SIGN}, but due to the current wind ' \ f'conditions (which is {wind_speed} miles per hour), it feels like {temp_feel_f}\N{DEGREE SIGN}.' \ f' {weather_suggest}' else: output = f'The weather in {city} is {feeling} {temp_f}\N{DEGREE SIGN}, and it currently feels like ' \ f'{temp_feel_f}\N{DEGREE SIGN}. {weather_suggest}' else: output = f'The weather in {weather_location} is {temp_f}\N{DEGREE SIGN}F, with a high of {high}, and a low ' \ f'of {low}. It currently feels Like {temp_feel_f}\N{DEGREE SIGN}F, and the current condition is ' \ f'{condition}. Sunrise at {sunrise}. Sunset at {sunset}.' if 'alerts' in response: alerts = response['alerts'][0]['event'] start_alert = datetime.fromtimestamp( response['alerts'][0]['start']).strftime("%I:%M %p") end_alert = datetime.fromtimestamp( response['alerts'][0]['end']).strftime("%I:%M %p") else: alerts, start_alert, end_alert = None, None, None if alerts and start_alert and end_alert: output += f' You have a weather alert for {alerts} between {start_alert} and {end_alert}' speaker.speak(text=output)