Beispiel #1
0
    def store_backlight_settings(self):
        thermostat_response = self.ecobee_service.request_thermostats(
            peb.Selection(selection_type=peb.SelectionType.REGISTERED.value, selection_match='',
                          include_settings=True)
        )

        bl_settings: peb.Settings = thermostat_response.thermostat_list[0].settings
        new_bl_settings = peb.Settings()
        backlight_keys = [k for k in new_bl_settings.attribute_name_map.keys() if 'backlight' in k and k.lower() == k]
        different = False
        for k in backlight_keys:
            set = getattr(bl_settings, k)
            if set != getattr(self._backlight_off, k):
                different = True
            setattr(new_bl_settings, k, set)
        if not different:
            logger.debug('not saving, backlight already off')
        else:
            logger.debug('saving backlight settings')
            self.backlight_settings = new_bl_settings
            self.persist_to_shelf()
Beispiel #2
0
 def set_fan_min_on_time(self, min_on_time):
     self._set_settings(
         peb.Settings(fan_min_on_time=int(min_on_time))
     )
Beispiel #3
0
 def set_humidity(self, rh):
     self._set_settings(
         settings=peb.Settings(humidity=int(rh))
     )
Beispiel #4
0
 def set_humidity_mode(self, mode):
     self._set_settings(
         peb.Settings(humidifier_mode=mode)
     )
Beispiel #5
0
class EcobeeData:
    _ecobee_service: peb.EcobeeService = None
    _authorize_response: peb.EcobeeAuthorizeResponse = None
    _authorize_expires: datetime = None
    _shelf_filename: str = None
    _backlight_settings: peb.Settings = None
    _got_token = False
    _exit_event: Event = None

    _backlight_on = peb.Settings(backlight_off_during_sleep=False,
                                 backlight_off_time=20,
                                 backlight_sleep_intensity=1,
                                 backlight_on_intensity=9
                                 )

    _backlight_off = peb.Settings(backlight_off_during_sleep=True,
                                  backlight_off_time=0,
                                  backlight_sleep_intensity=0,
                                  backlight_on_intensity=0
                                  )

    def __init__(self, shelf_filename, thermostat_name, ecobee_api_key, exit_event):
        self._exit_event = exit_event
        self._shelf_filename = shelf_filename
        pyecobee_db: shelve.DbfilenameShelf = None
        try:
            pyecobee_db = shelve.open(shelf_filename, protocol=2)
            data: EcobeeData = pyecobee_db[thermostat_name]
            self.__setstate__(data.__getstate__())
        except KeyError:
            # application_key = input('Please enter the API key of your ecobee App: ')
            self._ecobee_service = peb.EcobeeService(thermostat_name=thermostat_name, application_key=ecobee_api_key)
        finally:
            pyecobee_db.close()

    def __getstate__(self):
        return (self._ecobee_service,
                self._authorize_response,
                self._authorize_expires,
                self._backlight_settings,
                self._got_token)

    def __setstate__(self, state):
        self._ecobee_service, self._authorize_response, self._authorize_expires, self._backlight_settings, self._got_token = state

    # <editor-fold desc="Properties">
    @property
    def got_token(self):
        return self._got_token

    @property
    def ecobee_service(self):
        return self._ecobee_service

    @ecobee_service.setter
    def ecobee_service(self, value):
        self._ecobee_service = value

    @property
    def authorize_response(self):
        return self._authorize_response

    @authorize_response.setter
    def authorize_response(self, value):
        self._authorize_response = value

    @property
    def authorize_expires(self):
        return self._authorize_expires

    @authorize_expires.setter
    def authorize_expires(self, value):
        self._authorize_expires = value

    @property
    def backlight_settings(self):
        return self._backlight_settings

    @backlight_settings.setter
    def backlight_settings(self, value):
        self._backlight_settings = value

    @property
    def sensors(self):
        thermostat_response = self.ecobee_service.request_thermostats(
            peb.Selection(selection_type=peb.SelectionType.REGISTERED.value, selection_match='',
                          include_sensors=True)
        )
        sensors = thermostat_response.thermostat_list[0].remote_sensors
        return sensors

    @property
    def sensor_temps(self):
        temps = {}
        for sensor in self.sensors:
            sensor_name = sensor.name
            caps = [s for s in sensor.capability if s.type == 'temperature']
            if len(caps) > 0:
                temps[sensor_name] = float(caps[0].value) / 10.0

        return temps

    # </editor-fold>

    def persist_to_shelf(self):
        shelf = shelve.open(self._shelf_filename, protocol=2)
        shelf[self.ecobee_service.thermostat_name] = self
        shelf.close()

    def refresh_tokens(self):
        response = self.ecobee_service.refresh_tokens()
        logger.debug('TokenResponse returned from ecobee_service.refresh_tokens():\n{0}'.format(
            response.pretty_format()))
        self.persist_to_shelf()

    def authorize(self):
        self.authorize_response = self.ecobee_service.authorize()
        logger.debug('AutorizeResponse returned from ecobee_service.authorize():\n{0}'.format(
            self.authorize_response.pretty_format()))
        self.authorize_expires = datetime.utcnow() + \
                                 timedelta(minutes=self.authorize_response.expires_in)
        sys.stdout.flush()
        sys.stderr.flush()
        logger.info('Please goto ecobee.com, login to the web portal and click on the settings tab. Ensure the My '
                    'Apps widget is enabled. If it is not click on the My Apps option in the menu on the left. In the '
                    'My Apps widget paste "{0}" and in the textbox labelled "Enter your 4 digit pin to '
                    'install your third party app" and then click "Install App". The next screen will display any '
                    'permissions the app requires and will ask you to click "Authorize" to add the application.\n\n'.format(
            self.authorize_response.ecobee_pin))
        self.persist_to_shelf()
        wait(self.authorize_response.interval, self._exit_event, interval=5,
             extra_message=" waiting, please enter '{}'...".format(self.authorize_response.ecobee_pin))

    def wait_for_token(self):
        while datetime.utcnow() < self.authorize_expires and not self._exit_event.is_set():
            try:
                token_response = self.ecobee_service.request_tokens()
                logger.debug("Got token:\n%s", token_response.pretty_format())
                self.persist_to_shelf()
                break
            except peb.EcobeeAuthorizationException as e:
                if "authorization_pending" in e.error:
                    wait(self.authorize_response.interval, self._exit_event,
                         extra_message=" waiting, please enter '{}'...".format(self.authorize_response.ecobee_pin))
                else:
                    raise e
        else:
            logger.info("Authorization token expired, trying again")
            self.authorize()
            self.wait_for_token()

    def get_token(self, fail_fast=False):
        if not self.ecobee_service.authorization_token:
            if fail_fast: return False
            self.authorize()
        if not self.ecobee_service.access_token:
            if fail_fast: return False
            self.wait_for_token()

        now_utc = datetime.now(pytz.utc)
        if now_utc > self.ecobee_service.refresh_token_expires_on:
            if fail_fast: return False
            self.authorize()
            self.wait_for_token()
        elif now_utc > self.ecobee_service.access_token_expires_on:
            self.refresh_tokens()

        self._got_token = True
        return True

    def set_humidity(self, rh):
        self._set_settings(
            settings=peb.Settings(humidity=int(rh))
        )

    def set_humidity_auto(self):
        self.set_humidity_mode('auto')

    def _selection(self, **kwargs):
        return peb.Selection(selection_type=peb.SelectionType.REGISTERED.value, selection_match='', **kwargs)

    def _get_selection(self, selection, **kwargs):
        return self.ecobee_service.request_thermostats(selection)

    def set_humidity_mode(self, mode):
        self._set_settings(
            peb.Settings(humidifier_mode=mode)
        )

    def get_humidity_mode(self):
        thermostat_response = self.ecobee_service.request_thermostats(
            peb.Selection(selection_type=peb.SelectionType.REGISTERED.value, selection_match='',
                          include_settings=True)
        )
        return thermostat_response.thermostat_list[0].settings.humidifier_mode

    def set_fan_min_on_time(self, min_on_time):
        self._set_settings(
            peb.Settings(fan_min_on_time=int(min_on_time))
        )

    def store_backlight_settings(self):
        thermostat_response = self.ecobee_service.request_thermostats(
            peb.Selection(selection_type=peb.SelectionType.REGISTERED.value, selection_match='',
                          include_settings=True)
        )

        bl_settings: peb.Settings = thermostat_response.thermostat_list[0].settings
        new_bl_settings = peb.Settings()
        backlight_keys = [k for k in new_bl_settings.attribute_name_map.keys() if 'backlight' in k and k.lower() == k]
        different = False
        for k in backlight_keys:
            set = getattr(bl_settings, k)
            if set != getattr(self._backlight_off, k):
                different = True
            setattr(new_bl_settings, k, set)
        if not different:
            logger.debug('not saving, backlight already off')
        else:
            logger.debug('saving backlight settings')
            self.backlight_settings = new_bl_settings
            self.persist_to_shelf()

    def _set_settings(self, settings):
        sel = self._selection()
        ident=self._get_selection(sel).thermostat_list[0].identifier
        thermostat_response = self.ecobee_service.update_thermostats(
            selection=sel,
            thermostat=peb.Thermostat(identifier=ident,
                                      settings=settings)
        )
        logger.debug(thermostat_response.pretty_format())

    def turn_backlight_off(self):
        self._set_settings(self._backlight_off)

    def turn_backlight_on(self):
        self._set_settings(self.backlight_settings)

    def get_cur_inside_temp(self):
        thermostat_response = self.ecobee_service.request_thermostats(
            peb.Selection(selection_type=peb.SelectionType.REGISTERED.value, selection_match='',
                          include_runtime=True)
        )
        inside_temp = thermostat_response.thermostat_list[0].runtime.actual_temperature / 10.0
        des_inside_temp = thermostat_response.thermostat_list[0].runtime.desired_heat / 10.0
        return float(inside_temp), float(des_inside_temp)

    def get_cur_inside_humidity(self):
        thermostat_response = self.ecobee_service.request_thermostats(
            peb.Selection(selection_type=peb.SelectionType.REGISTERED.value, selection_match='',
                          include_runtime=True)
        )
        humidity = thermostat_response.thermostat_list[0].runtime.actual_humidity
        return float(humidity)

    def get_cur_hvac_mode(self):
        thermostat_response = self.ecobee_service.request_thermostats(
            peb.Selection(selection_type=peb.SelectionType.REGISTERED.value, selection_match='',
                          include_equipment_status=True)
        )
        return thermostat_response.thermostat_list[0].equipment_status

    def get_fan_min_on_time(self):
        thermostat_response = self.ecobee_service.request_thermostats(
            peb.Selection(selection_type=peb.SelectionType.REGISTERED.value, selection_match='',
                          include_settings=True)
        )
        return thermostat_response.thermostat_list[0].settings.fan_min_on_time

    def occupied(self):
        thermostat_response = self.ecobee_service.request_thermostats(
            selection=peb.Selection(selection_type=peb.SelectionType.REGISTERED.value, selection_match='',
                                    include_sensors=True, include_program=True, include_events=True)
        )
        if thermostat_response.thermostat_list[0].program.current_climate_ref in ['home', 'sleep']:
            return True

        for sensor in thermostat_response.thermostat_list[0].remote_sensors:
            caps = [a.value == 'true' for a in sensor.capability if a.type == 'occupancy']
            if any(caps):
                return True
        for event in thermostat_response.thermostat_list[0].events:
            if event.running and (event.heat_hold_temp > 640 or event.cool_hold_temp < 760):
                return True
        return False

    def get_future_set_temp(self):

        thermostat_response = self.ecobee_service.request_thermostats(
            selection=peb.Selection(selection_type=peb.SelectionType.REGISTERED.value, selection_match='',
                                    include_program=True, include_events=True)
        )
        thermostat = thermostat_response.thermostat_list[0]
        therm_time = datetime.strptime(thermostat.thermostat_time, '%Y-%m-%d %H:%M:%S')
        future_time = therm_time + timedelta(hours=1)
        day_of_week = future_time.weekday()
        time_of_day = math.floor((future_time.hour * 60 + future_time.minute) / 30)
        future_climate = thermostat.program.schedule[day_of_week][time_of_day]
        future_temp = [c.heat_temp / 10.0
                       for c in thermostat.program.climates
                       if c.climate_ref == future_climate
                       ][0]
        logger.debug('future temp based on schedule: %s', future_temp)
        current_event = [e for e in thermostat.events if e.running]
        if current_event:
            ce = current_event[0]
            end_event = datetime.strptime('{} {}'.format(ce.end_date, ce.end_time), '%Y-%m-%d %H:%M:%S')
            if end_event > future_time:
                future_temp = ce.heat_hold_temp / 10.0
                logger.debug('Using Override Temp: %s', future_temp)
        return future_temp
        # logger.debug(thermostat_response.thermostat_list[0]
        #              .pretty_format())

    def graceful_shutdown(self):
        if self.got_token:
            self.turn_backlight_on()
            self.set_fan_min_on_time(20)
            self.set_humidity_auto()
        self.persist_to_shelf()