#!/home/pi/home-automation/venv/bin/python import os import time as mytime from pprint import pprint from pyowm import OWM from pyhubitat import MakerAPI import astropy.units as u from astropy.time import Time from datetime import datetime from astropy.coordinates import EarthLocation, AltAz from astropy.coordinates import get_sun OWM_KEY = os.environ['OWM_KEY'] HUB_TOKEN = os.environ['HUBITAT_TOKEN'] HUB_IP = os.environ['HUBITAT_IP'] HUB_APP_ID = os.environ['HUBITAT_APP_ID'] LATITUDE = float(os.environ['LATITUDE']) LONGITUDE = float(os.environ['LONGITUDE']) HUB_URL = f'https://{HUB_IP}/apps/api/{HUB_APP_ID}' def filter_open_windows(blinds_to_lower): filtered_blinds_to_lower = [] blind_to_sensor_mapping = { 371: [402], # office east 372: [403], # office south 373: [404], # laundry 376: [405, 406], # sam south 377: [407], # annie south 378: [408], # annie west 379: [410], # primary west 380: [409] # primary north } ph = MakerAPI(HUB_TOKEN, HUB_URL) for blind in blinds_to_lower: found_open_window = False if blind in blind_to_sensor_mapping: for sensor in blind_to_sensor_mapping[blind]: device_details = ph.get_device_info(sensor) mytime.sleep(2) # throttle API calls current_state = next((elm['currentValue'] for elm in device_details['attributes'] if elm['name'] == 'contact'), None) if current_state == 'open': found_open_window = True if not found_open_window: filtered_blinds_to_lower.append(blind) return filtered_blinds_to_lower def set_blinds(blinds_to_lower): ph = MakerAPI(HUB_TOKEN, HUB_URL) devices = ph.list_devices() blinds = [d for d in devices if d['name'] == "Somfy MyLink Shade"] pprint(blinds) directions = blinds_to_lower.split() blind_ids_to_lower = [] for elm in [b for b in blinds for direction in directions if direction in b['label']]: blind_ids_to_lower.append(int(elm['id'])) blind_ids = [int(elm['id']) for elm in blinds] blind_ids_to_raise = list(set(blind_ids) - set(blind_ids_to_lower)) blind_ids_to_lower = filter_open_windows(blind_ids_to_lower) pprint(blind_ids_to_raise) pprint(blind_ids_to_lower) for id in blind_ids_to_lower: ph.send_command(id, 'close') mytime.sleep(2) # throttle API calls for id in blind_ids_to_raise: ph.send_command(id, 'open') mytime.sleep(2) # throttle API calls # get sun angles etc at our location briar_house = EarthLocation.from_geodetic(LONGITUDE, LATITUDE, 0) utcoffset = -4 * u.hour # Eastern Daylight Time time = Time(datetime.utcnow(), scale='utc', location=briar_house) aaframe = AltAz(obstime=time, location=briar_house) sun = get_sun(time) sunaa = sun.transform_to(aaframe) blinds_to_lower = '' # if sun is up if sunaa.alt > 5.0 * u.deg: # determine sun's angle with house house_angle = 145 * u.deg front_angle = sunaa.az - house_angle if front_angle < 0: front_angle = front_angle + 360 * u.deg # get weather owm = OWM(OWM_KEY) mgr = owm.weather_manager() observation = mgr.weather_at_place('Provo,UT,USA') w = observation.weather print(f"temperature = {w.temperature('fahrenheit')}") print(f"wind = {w.wind(unit='beaufort')}") print(f"clouds = {w.clouds}") if w.temperature('fahrenheit')['temp'] > 50: if w.wind(unit='beaufort')['speed'] <= 6: if w.clouds < 80 or w.temperature('fahrenheit')['temp'] > 60: blinds_to_lower = '' if (front_angle.is_within_bounds('270d', '360d') or front_angle.is_within_bounds('0d', '90d')): # front blinds_to_lower = 'South' if (front_angle.is_within_bounds('0d', '180d')): # right if len(blinds_to_lower) > 0: blinds_to_lower += ' ' blinds_to_lower += 'West' if (front_angle.is_within_bounds('90d', '270d')): # back if len(blinds_to_lower) > 0: blinds_to_lower += ' ' blinds_to_lower += 'North' if (front_angle.is_within_bounds('180d', '360d')): # left if len(blinds_to_lower) > 0: blinds_to_lower += ' ' blinds_to_lower += 'East' set_blinds(blinds_to_lower)