Пример #1
0
async def main(loop):
    def parse_recursive(msg):
        if 'type' in msg and msg['type'] == 'refEnum':
            for reference in msg['references']:
                client.get(reference['id'])
        else:
            pprint(msg)

    def parse_message(msg):
        pprint(msg)

    client = NefitCore(serial_number=SERIAL_NUMBER,
                       access_key=ACCESS_KEY,
                       password=PASSWORD,
                       message_callback=parse_message)
    client.connect()
    loop.nefitclient = client

    await client.xmppclient.connected_event.wait()

    client.get('/ecus/rrc/uiStatus')
Пример #2
0
class NefitEasy:
    def __init__(self, hass, credentials):
        from aionefit import NefitCore
        _LOGGER.debug("Initialize Nefit class")

        self.data = {}
        self.ids = {}
        self.hass = hass
        self.error_state = "initializing"
        self.events = {'/ecus/rrc/uiStatus': asyncio.Event()}

        self.nefit = NefitCore(serial_number=credentials.get(CONF_SERIAL),
                               access_key=credentials.get(CONF_ACCESSKEY),
                               password=credentials.get(CONF_PASSWORD),
                               message_callback=self.parse_message)

        self.nefit.failed_auth_handler = self.failed_auth_handler
        self.nefit.no_content_callback = self.no_content_callback

    async def connect(self):
        self.nefit.connect()
        _LOGGER.debug("Waiting for connected event")
        try:
            # await self.nefit.xmppclient.connected_event.wait()
            await asyncio.wait_for(
                self.nefit.xmppclient.connected_event.wait(), timeout=15.0)
            _LOGGER.debug("adding stop listener")
            self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP,
                                            self.shutdown)
        except concurrent.futures._base.TimeoutError:
            _LOGGER.debug("TimeoutError on waiting for connected event")
            self.hass.components.persistent_notification.create(
                'Timeout while connecting to Bosch cloud. Retrying in the background',
                title='Nefit error',
                notification_id='nefit_logon_error')
            raise PlatformNotReady

        if self.error_state == "authentication_failed":
            self.hass.components.persistent_notification.create(
                'Invalid credentials while connecting to Bosch cloud.',
                title='Nefit error',
                notification_id='nefit_logon_error')
            raise PlatformNotReady

    def shutdown(self, event):
        _LOGGER.debug("Shutdown connection to Bosch cloud")
        self.nefit.disconnect()

    def no_content_callback(self, data):
        _LOGGER.debug("no_content_callback: %s", data)

    def failed_auth_handler(self, event):
        self.error_state = "authentication_failed"
        self.nefit.xmppclient.connected_event.set()

    def parse_message(self, data):
        """Message received callback function for the XMPP client.
        """
        _LOGGER.debug("parse_message callback called with data %s", data)
        if not 'id' in data:
            _LOGGER.error("Unknown response received: %s", data)
            return

        if data['id'] == '/ecus/rrc/uiStatus':
            self.data['uistatus'] = data['value']
            self.data['temp_setpoint'] = float(
                data['value']['TSP'])  #for climate
            self.data['inhouse_temperature'] = float(
                data['value']['IHT'])  #for climate
            self.data['user_mode'] = data['value']['UMD']  #for climate
            self.data['boiler_indicator'] = data['value']['BAI']  #for climate
            self.data['last_update'] = data['value']['CTD']

        elif data['id'] == '/dhwCircuits/dhwA/dhwOperationClockMode' or data[
                'id'] == '/dhwCircuits/dhwA/dhwOperationManualMode':
            self.data['hot_water'] = data['value']
            self.events['hot_water'].set()

        if data['id'] in self.events:
            if data['id'] in self.ids:
                self.data[self.ids[data['id']]] = data['value']

            # Mark event as finished
            self.events[data['id']].set()
Пример #3
0
class NefitThermostat(ClimateDevice):
    """Representation of a NefitThermostat device."""

    def __init__(self, hass, config):
        from aionefit import NefitCore
        """Initialize the thermostat."""
        name = config.get(CONF_NAME)
        serial = config.get(CONF_SERIAL)
        accesskey = config.get(CONF_ACCESSKEY)
        password = config.get(CONF_PASSWORD)

        self.config = config
        self.hass = hass
        self._name = name

        self.error_state = "initializing"
        self._online = False
        self._unit_of_measurement = TEMP_CELSIUS
        self._uistatus = None
        self._attributes = {}
        self._stateattr = {}
        self._data = {}
        self._hvac_modes = [HVAC_MODE_HEAT]
        self._url_events = {
            '/ecus/rrc/uiStatus': asyncio.Event(),
            '/heatingCircuits/hc1/actualSupplyTemperature': asyncio.Event(),
            '/system/sensors/temperatures/outdoor_t1': asyncio.Event(),
            '/system/appliance/systemPressure': asyncio.Event(),
            '/ecus/rrc/recordings/yearTotal': asyncio.Event()
        }

        self._client = NefitCore(serial_number=serial,
                       access_key=accesskey,
                       password=password,
                       message_callback=self.parse_message)
        
        self._client.failed_auth_handler = self.failed_auth_handler
        self._client.no_content_callback = self.no_content_callback

    async def connect(self):
        self._client.connect()
        _LOGGER.debug("Waiting for connected event")        
        try:
            # await self._client.xmppclient.connected_event.wait()
            await asyncio.wait_for(self._client.xmppclient.connected_event.wait(), timeout=10.0)
            _LOGGER.debug("adding stop listener")
            self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP,
                                            self._shutdown)
        except concurrent.futures._base.TimeoutError:
            _LOGGER.debug("TimeoutError on waiting for connected event")
            self.hass.components.persistent_notification.create( 
                'Timeout while connecting to Bosch cloud. Retrying in the background',
                title='Nefit error',
                notification_id='nefit_logon_error')
            raise PlatformNotReady
        
        if self.error_state == "authentication_failed":
            self.hass.components.persistent_notification.create( 
                'Invalid credentials while connecting to Bosch cloud.',
                title='Nefit error',
                notification_id='nefit_logon_error')
            raise PlatformNotReady

    def no_content_callback(self, data):
        _LOGGER.debug("no_content_callback: %s", data)

    def failed_auth_handler(self, event):
        self.error_state = "authentication_failed"
        self._client.xmppclient.connected_event.set()

    @property
    def supported_features(self):
        """Return the list of supported features.
        """
        return SUPPORT_FLAGS

    @property
    def target_temperature_step(self):
        return 0.5

    def parse_message(self, data):
        """Message received callback function for the XMPP client.
        """
        _LOGGER.debug("parse_message callback called with data %s", data)
        if not 'id' in data:
            _LOGGER.error("Unknown response received: %s", data)
            return

        if data['id'] == '/ecus/rrc/uiStatus':
            self._uistatus = data['value']
            self._data['temp_setpoint'] = float(data['value']['TSP'])
            self._data['inhouse_temperature'] = float(data['value']['IHT'])
            self._data['user_mode'] = data['value']['UMD']
            self._stateattr['boiler_indicator'] = data['value']['BAI']
            self._stateattr['current_time'] = data['value']['CTD']        
        elif data['id'] == '/heatingCircuits/hc1/actualSupplyTemperature':
            self._stateattr['supply_temperature'] = data['value']
        elif data['id'] == '/system/sensors/temperatures/outdoor_t1':
            self._stateattr['outdoor_temperature'] = data['value']
        elif data['id'] == '/system/appliance/systemPressure':
            self._stateattr['system_pressure'] = data['value']            
        elif data['id'] == '/ecus/rrc/recordings/yearTotal':
            self._stateattr['year_total'] = data['value']

        if data['id'] in self._url_events:
            self._url_events[data['id']].set()
            
    async def async_update(self):
        """Get latest data
        """
        _LOGGER.debug("async_update called")
        tasks = []
        for url in self._url_events:
            event = self._url_events[url]
            event.clear()
            self._client.get(url)
            tasks.append(asyncio.wait_for(event.wait(), timeout=10))
        
        await asyncio.gather(*tasks)
    
        _LOGGER.debug("async_update finished")

    @property
    def name(self):
        """Return the name of the ClimateDevice.
        """
        return self._name

    @property
    def temperature_unit(self):
        """Return the unit of measurement.
        """
        return self._unit_of_measurement

    @property
    def current_temperature(self):
        """Return the current temperature.
        """
        return self._data.get('inhouse_temperature')

    @property
    def target_temperature(self):
        return self._data.get('temp_setpoint')

    @property
    def hvac_modes (self):
        """List of available modes."""
        return self._hvac_modes

    @property
    def hvac_mode(self):
        return HVAC_MODE_HEAT
    
    @property
    def hvac_action(self):
        """Return the current running hvac operation if supported."""
        if self._stateattr.get('boiler_indicator') == 'CH': #HW (hot water) is not for climate
            return CURRENT_HVAC_HEAT
        
        return CURRENT_HVAC_IDLE

    @property
    def preset_modes(self):
        """Return available preset modes."""
        return [
            OPERATION_CLOCK
        ]


    @property
    def preset_mode(self):
        """Return the current preset mode."""
        if self._data.get('user_mode') == 'manual':
            return None
        elif self._data.get('user_mode') == 'clock':
            return OPERATION_CLOCK
        else:
            return None

    @property
    def device_state_attributes(self):
        """Return the device specific state attributes."""

        return self._stateattr

    @property
    def min_temp(self):
        """Return the minimum temperature."""
        return self.config.get(CONF_MIN_TEMP)

    @property
    def max_temp(self):
        """Return the maximum temperature."""
        return self.config.get(CONF_MAX_TEMP)

    async def async_set_preset_mode(self, preset_mode):
        """Set new target operation mode."""
        _LOGGER.debug("set_preset_mode called mode={}.".format(preset_mode))
        if preset_mode == OPERATION_CLOCK:
            new_mode = "clock"
        else:
            new_mode = "manual"

        self._client.set_usermode(new_mode)
        await asyncio.wait_for(self._client.xmppclient.message_event.wait(), timeout=10.0)
        self._client.xmppclient.message_event.clear()
        self._data['user_mode'] = new_mode

    async def async_set_temperature(self, **kwargs):
        """Set new target temperature."""
        temperature = kwargs.get(ATTR_TEMPERATURE)
        self._data['target_temperature'] = temperature
        _LOGGER.debug("set_temperature called (temperature={}).".format(temperature))
        self._client.set_temperature(temperature)
        await asyncio.wait_for(self._client.xmppclient.message_event.wait(), timeout=10.0)
        self._client.xmppclient.message_event.clear()
        self._data['target_temperature'] = temperature
        

    def _shutdown(self, event):
        _LOGGER.debug("shutdown")
        self._client.disconnect()
Пример #4
0
class NefitEasy:
    def __init__(self, hass, credentials):
        from aionefit import NefitCore
        _LOGGER.debug("Initialize Nefit class")

        self.data = {} #stores device states and values
        self.keys = {} #unique name for entity
        self.events = {}
        self.uiStatusVars = {} #variables to monitor for sensors
        self.hass = hass
        self.connected_state = STATE_INIT
        
        self.nefit = NefitCore(serial_number=credentials.get(CONF_SERIAL),
                       access_key=credentials.get(CONF_ACCESSKEY),
                       password=credentials.get(CONF_PASSWORD),
                       message_callback=self.parse_message)
        
        self.nefit.failed_auth_handler = self.failed_auth_handler
        self.nefit.no_content_callback = self.no_content_callback

    async def connect(self):
        self.nefit.connect()
        _LOGGER.debug("Waiting for connected event")        
        try:
            # await self.nefit.xmppclient.connected_event.wait()
            await asyncio.wait_for(self.nefit.xmppclient.connected_event.wait(), timeout=60.0)
            self.connected_state = STATE_CONNECTED
            _LOGGER.debug("adding stop listener")
            self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP,
                                            self.shutdown)
        except concurrent.futures._base.TimeoutError:
            _LOGGER.debug("TimeoutError on waiting for connected event")
            self.hass.components.persistent_notification.create( 
                'Timeout while connecting to Bosch cloud.',
                title='Nefit error',
                notification_id='nefit_logon_error')
            raise PlatformNotReady
        
        if self.connected_state == STATE_ERROR_AUTH:
            self.hass.components.persistent_notification.create( 
                'Invalid credentials while connecting to Bosch cloud.',
                title='Nefit error',
                notification_id='nefit_logon_error')
            raise PlatformNotReady

    def shutdown(self, event):
        _LOGGER.debug("Shutdown connection to Bosch cloud")
        self.nefit.disconnect()

    def no_content_callback(self, data):
        _LOGGER.debug("no_content_callback: %s", data)

    def failed_auth_handler(self, event):
        self.connected_state = STATE_ERROR_AUTH
        self.nefit.xmppclient.connected_event.set()

    def parse_message(self, data):
        """Message received callback function for the XMPP client.
        """
        _LOGGER.debug("parse_message data %s", data)
        if not 'id' in data:
            _LOGGER.error("Unknown response received: %s", data)
            return

        if data['id'] in self.keys:
            key = self.keys[data['id']]
            _LOGGER.debug("Got update for %s.", key)

            if data['id'] == '/ecus/rrc/uiStatus':
                self.data['temp_setpoint'] = float(data['value']['TSP']) #for climate
                self.data['inhouse_temperature'] = float(data['value']['IHT'])  #for climate
                self.data['user_mode'] = data['value']['UMD']  #for climate
                self.data['boiler_indicator'] = data['value']['BAI']  #for climate
                self.data['last_update'] = data['value']['CTD']

                for uikey in self.uiStatusVars:
                    self.updateDeviceValue(uikey, data['value'].get(self.uiStatusVars[uikey]))

            self.updateDeviceValue(key, data['value'])

            # Mark event as finished if it was part of an update action
            if key in self.events:
                self.events[key].set()

    def updateDeviceValue(self, key, value):
        """Store new device value and send to dispatcher to be picked up by device"""
        self.data[key] = value

        #send update signal to dispatcher to pick up new state
        signal = DISPATCHER_ON_DEVICE_UPDATE.format(key=key)
        async_dispatcher_send(self.hass, signal)
Пример #5
0
class NefitEasy:
    def __init__(self, hass, credentials):
        from aionefit import NefitCore
        _LOGGER.debug("Initialize Nefit class")

        self.data = {}  #stores device states and values
        self.keys = {}  #unique name for entity
        self.events = {}
        self.uiStatusVars = {}  #variables to monitor for sensors
        self.hass = hass
        self.connected_state = STATE_INIT
        self.expected_end = False
        self.serial = credentials.get(CONF_SERIAL)

        self.nefit = NefitCore(serial_number=credentials.get(CONF_SERIAL),
                               access_key=credentials.get(CONF_ACCESSKEY),
                               password=credentials.get(CONF_PASSWORD),
                               message_callback=self.parse_message)

        self.nefit.failed_auth_handler = self.failed_auth_handler
        self.nefit.no_content_callback = self.no_content_callback
        self.nefit.session_end_callback = self.session_end_callback

    async def connect(self):
        retriesA = 0
        while self.connected_state != STATE_CONNECTION_VERIFIED and retriesA < 3:
            self.nefit.connect()
            _LOGGER.debug("Waiting for connected event")
            try:
                await asyncio.wait_for(
                    self.nefit.xmppclient.connected_event.wait(), timeout=60.0)
                self.connected_state = STATE_CONNECTED
                _LOGGER.debug("adding stop listener")
                self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP,
                                                self.shutdown)
            except concurrent.futures._base.TimeoutError:
                _LOGGER.debug("TimeoutError on waiting for connected event")
                retriesA = retriesA + 1

            #test password for decrypting messages
            if self.connected_state == STATE_CONNECTED:
                _LOGGER.info("Testing connection")
                retriesB = 0
                while self.connected_state != STATE_CONNECTION_VERIFIED and retriesB < 3:
                    try:
                        self.nefit.get('/gateway/brandID')
                        await asyncio.wait_for(
                            self.nefit.xmppclient.message_event.wait(),
                            timeout=30.0)
                        self.connected_state = STATE_CONNECTION_VERIFIED
                        self.nefit.xmppclient.message_event.clear()
                        _LOGGER.info(
                            "Connected %s with %d retries and %d test retries.",
                            self.serial, retriesA, retriesB)
                    except concurrent.futures._base.TimeoutError:
                        _LOGGER.error(
                            "Did not get a response in time for testing connection."
                        )
                        retriesB = retriesB + 1
                    except:
                        _LOGGER.error(
                            "No connection while testing connection.")
                        break

        if self.connected_state != STATE_CONNECTION_VERIFIED:
            self.hass.components.persistent_notification.create(
                'Did not succeed in connecting {} to Bosch cloud after retrying 3 times.'
                .format(self.serial),
                title='Nefit connect error',
                notification_id='nefit_connect_error')

    def shutdown(self, event):
        _LOGGER.debug("Shutdown connection to Bosch cloud")
        self.expected_end = True
        self.nefit.disconnect()

    def no_content_callback(self, data):
        _LOGGER.debug("no_content_callback: %s", data)

    def failed_auth_handler(self, event):
        self.connected_state = STATE_ERROR_AUTH
        self.nefit.xmppclient.connected_event.set()

        #disconnect, since nothing will work from now.
        self.shutdown('auth_failed')

        if event == 'auth_error_password':
            self.hass.components.persistent_notification.create(
                'Invalid password for connecting {} to Bosch cloud.'.format(
                    self.serial),
                title='Nefit password error',
                notification_id='nefit_password_error')
        else:
            self.hass.components.persistent_notification.create(
                'Invalid credentials (serial or accesskey) for connecting {} to Bosch cloud.'
                .format(self.serial),
                title='Nefit authentication error',
                notification_id='nefit_logon_error')

    def session_end_callback(self):
        """If connection is closed unexpectedly, try to reconnect"""
        if not self.expected_end:
            self.hass.components.persistent_notification.create(
                'Unexpected disconnect of {} with Bosch server'.format(
                    self.serial),
                title='Nefit disconnect',
                notification_id='nefit_disconnect')
            #loop = asyncio.get_event_loop()
            #loop.run_until_complete(self.connect())
            #loop.close()

    def parse_message(self, data):
        """Message received callback function for the XMPP client."""
        _LOGGER.debug("parse_message data %s", data)
        if not 'id' in data:
            _LOGGER.error("Unknown response received: %s", data)
            return

        if data['id'] in self.keys:
            key = self.keys[data['id']]
            _LOGGER.debug("Got update for %s.", key)

            if data['id'] == '/ecus/rrc/uiStatus' and self.connected_state == STATE_CONNECTION_VERIFIED:
                self.data['temp_setpoint'] = float(
                    data['value']['TSP'])  #for climate
                self.data['inhouse_temperature'] = float(
                    data['value']['IHT'])  #for climate
                self.data['user_mode'] = data['value']['UMD']  #for climate
                self.data['boiler_indicator'] = data['value'][
                    'BAI']  #for climate
                self.data['last_update'] = data['value']['CTD']

                # Update all sensors/switches when there is new data form uiStatus
                for uikey in self.uiStatusVars:
                    self.updateDeviceValue(
                        uikey, data['value'].get(self.uiStatusVars[uikey]))

            self.updateDeviceValue(key, data['value'])

            # Mark event as finished if it was part of an update action
            if key in self.events:
                self.events[key].set()

    def updateDeviceValue(self, key, value):
        """Store new device value and send to dispatcher to be picked up by device"""
        self.data[key] = value

        #send update signal to dispatcher to pick up new state
        signal = DISPATCHER_ON_DEVICE_UPDATE.format(key=key)
        async_dispatcher_send(self.hass, signal)

    async def get_value(self, key, url):
        isNewKey = not url in self.keys
        if isNewKey:
            self.events[key] = asyncio.Event()
            self.keys[url] = key
        event = self.events[key]
        event.clear()  #clear old event
        self.nefit.get(url)
        await asyncio.wait_for(event.wait(), timeout=9)
        if isNewKey:
            del self.events[key]
            del self.keys[url]
        return self.data[key]