Exemplo n.º 1
0
class Folder(object):
    """
    Object representing a program folder on the ISY device.

    |  parent: The folder manager object.
    |  pid: The folder ID.
    |  pname: The folder name.
    |  pstatus: The current folder status.

    :ivar dtype: Returns the type of the object (folder).
    :ivar status: Watched property representing the current status of the
                  folder.
    """

    status = Property(0, readonly=True)
    dtype = 'folder'

    def __init__(self, parent, pid, pname, pstatus):
        self.noupdate = False
        self.parent = parent
        self.name = pname
        self._id = pid

        self.status.update(pstatus, force=True, silent=True)

    def __str__(self):
        """ Returns a string representation of the folder. """
        return 'Folder(' + self._id + ')'

    @property
    def leaf(self):
        return self

    def update(self, waitTime=0, data=None):
        """
        Updates the status of the program.

        |  data: [optional] The data to update the folder with.
        |  waitTime: [optional] Seconds to wait before updating.
        """
        if not self.noupdate:
            if data is not None:
                self.status.update(data['pstatus'], force=True, silent=True)
            else:
                self.parent.update(waitTime, pid=self._id)

    def run(self):
        """ Runs the appropriate clause of the object. """
        response = self.parent.parent.conn.programRun(self._id)

        if response is None:
            self.parent.parent.log.warning('ISY could not run program: ' +
                                           self._id)
            return False
        else:
            self.parent.parent.log.info('ISY ran program: ' + self._id)
            self.update(_change2update_interval)
            return True

    def runThen(self):
        """ Runs the THEN clause of the object. """
        response = self.parent.parent.conn.programRunThen(self._id)

        if response is None:
            self.parent.parent.log.warning(
                "ISY couldn't run then in program: " + self._id)
            return False
        else:
            self.parent.parent.log.info('ISY ran then in program: ' + self._id)
            self.update(_change2update_interval)
            return True

    def runElse(self):
        """ Runs the ELSE clause of the object. """
        response = self.parent.parent.conn.programRunElse(self._id)

        if response is None:
            self.parent.parent.log.warning(
                "ISY couldn't run else in program: " + self._id)
            return False
        else:
            self.parent.parent.log.info('ISY ran else in program: ' + self._id)
            self.update(_change2update_interval)

    def stop(self):
        """ Stops the object if it is running. """
        response = self.parent.parent.conn.programStop(self._id)

        if response is None:
            self.parent.parent.log.warning('ISY could not stop program: ' +
                                           self._id)
            return False
        else:
            self.parent.parent.log.info('ISY stopped program: ' + self._id)
            self.update(_change2update_interval)
            return True
Exemplo n.º 2
0
class Variable(object):
    """
    Object representing a variable on the controller.

    |  parent: The variable manager object.
    |  vid: List of variable IDs.
    |  vtype: List of variable types.
    |  init: List of values that variables initialize to when the controller
             starts.
    |  val: The current variable value.
    |  ts: The timestamp for the last time the variable was edited.

    :ivar init: Watched property that represents the value the variable
                initializes to when the controller boots.
    :ivar lastEdit: Watched property that indicates the last time the variable
                    was edited.
    :ivar val: Watched property that represents the value of the variable.
    """

    init = Property(0)
    val = Property(0)
    lastEdit = Property(_empty_time, readonly=True)

    def __init__(self, parent, vid, vtype, init, val, ts):
        super(Variable, self).__init__()
        self.noupdate = False
        self.parent = parent
        self._id = vid
        self._type = vtype

        self.init.update(init, force=True, silent=True)
        self.init.reporter = self.__report_init__
        self.val.update(val, force=True, silent=True)
        self.val.reporter = self.__report_val__
        self.lastEdit.update(ts, force=True, silent=True)

    def __str__(self):
        """ Returns a string representation of the variable. """
        return 'Variable(type=' + str(self._type) + ', id=' + str(self._id) \
            + ', val=' + str(self.val) + ')'

    def __repr__(self):
        """ Returns a string representation of the variable. """
        return str(self)

    def __report_init__(self, val):
        self.noupdate = True
        self.setInit(val)
        self.noupdate = False

    def __report_val__(self, val):
        self.noupdate = True
        self.setValue(val)
        self.noupdate = False

    def update(self, waitTime=0):
        """
        Updates the object with the variable's parameters from the controller.

        |  waitTime: Seconds to wait before updating.
        """
        if not self.noupdate:
            self.parent.update(waitTime)

    def setInit(self, val):
        """
        Sets the initial value for the variable after the controller boots.

        |  val: The value to have the variable initialize to.
        """
        response = self.parent.parent.conn.initVariable(
            self._type, self._id, val)
        if response is None:
            self.parent.parent.log.warning('ISY could not set variable init ' +
                                           'value: ' + str(self._type) + ', ' +
                                           str(self._id))
        else:
            self.parent.parent.log.info('ISY set variable init value: ' +
                                        str(self._type) + ', ' + str(self._id))
            self.update(_change2update_interval)

    def setValue(self, val):
        """
        Sets the value of the variable.

        |  val: The value to set the variable to.
        """
        response = self.parent.parent.conn.setVariable(self._type, self._id,
                                                       val)
        if response is None:
            self.parent.parent.log.warning('ISY could not set variable: ' +
                                           str(self._type) + ', ' +
                                           str(self._id))
        else:
            self.parent.parent.log.info('ISY set variable: ' +
                                        str(self._type) + ', ' + str(self._id))
            self.update(_change2update_interval)
Exemplo n.º 3
0
class Variable(object):

    init = Property(0)
    val = Property(0)
    lastEdit = Property(_empty_time, readonly=True)

    def __init__(self, parent, vid, vtype, init, val, ts):
        super(Variable, self).__init__()
        self.noupdate = False
        self.parent = parent
        self._id = vid
        self._type = vtype

        self.init.update(init, force=True, silent=True)
        self.init.reporter = self.__report_init__
        self.val.update(val, force=True, silent=True)
        self.val.reporter = self.__report_val__
        self.lastEdit.update(ts, force=True, silent=True)

    def __str__(self):
        return 'Variable(type=' + str(self._type) + ', id=' + str(self._id) \
            + ', val=' + str(self.val) + ')'

    def __repr__(self):
        return str(self)

    def __report_init__(self, val):
        self.noupdate = True
        self.setInit(val)
        self.noupdate = False

    def __report_val__(self, val):
        self.noupdate = True
        self.setValue(val)
        self.noupdate = False

    def update(self, waitTime=0):
        if not self.noupdate:
            self.parent.update(waitTime)

    def setInit(self, val):
        response = self.parent.parent.conn.initVariable(self._type,
                                                        self._id, val)
        if response is None:
            self.parent.parent.log.warning('ISY could not set variable init '
                                           + 'value: ' + str(self._type) + ', '
                                           + str(self._id))
        else:
            self.parent.parent.log.info('ISY set variable init value: '
                                        + str(self._type) + ', '
                                        + str(self._id))
            self.update(_change2update_interval)

    def setValue(self, val):
        response = self.parent.parent.conn.setVariable(self._type,
                                                       self._id, val)
        if response is None:
            self.parent.parent.log.warning('ISY could not set variable: '
                                           + str(self._type) + ', '
                                           + str(self._id))
        else:
            self.parent.parent.log.info('ISY set variable: ' + str(self._type)
                                        + ', ' + str(self._id))
            self.update(_change2update_interval)
Exemplo n.º 4
0
class Node(object):
    """
    This class handles ISY nodes.

    |  parent: The node manager object.
    |  nid: The Node ID.
    |  nval: The current Node value.
    |  name: The node name.
    |  [optional] dimmable: Default True. Boolean of whether the node is
       dimmable.
    |  spoken: The string of the Notes Spoken field.

    :ivar status: A watched property that indicates the current status of the
                  node.
    :ivar hasChildren: Property indicating that there are no more children.
    """

    status = Property(0)
    hasChildren = False

    def __init__(self,
                 parent,
                 nid,
                 nval,
                 name,
                 dimmable=True,
                 spoken=False,
                 notes=False,
                 uom=None,
                 prec=0,
                 aux_properties=None,
                 node_def_id=None,
                 parent_nid=None,
                 type=None):
        self.parent = parent
        self._id = nid
        self.dimmable = dimmable
        self.name = name
        self._notes = notes
        self.uom = uom
        self.prec = prec
        self._spoken = spoken
        self.aux_properties = aux_properties or {}
        self.node_def_id = node_def_id
        self.type = type

        if (parent_nid != nid):
            self.parent_nid = parent_nid
        else:
            self.parent_nid = None

        self.status = nval
        self.status.reporter = self.__report_status__

        self.controlEvents = EventEmitter()

    def __str__(self):
        """ Returns a string representation of the node. """
        return 'Node(' + self._id + ')'

    @property
    def nid(self):
        return self._id

    def __report_status__(self, new_val):
        self.on(new_val)

    def update(self, waitTime=0, hint=None):
        """ Update the value of the node from the controller. """
        if not self.parent.parent.auto_update:
            sleep(waitTime)
            xml = self.parent.parent.conn.updateNode(self._id)

            if xml is not None:
                try:
                    xmldoc = minidom.parseString(xml)
                except:
                    self.parent.parent.log.error('ISY Could not parse nodes,' +
                                                 'poorly formatted XML.')
                else:
                    state_val, state_uom, state_prec, aux_props = parse_xml_properties(
                        xmldoc)

                    self.aux_properties = {}
                    for prop in aux_props:
                        self.aux_properties[prop.get(ATTR_ID)] = prop

                    self.uom = state_uom
                    self.prec = state_prec
                    self.status.update(state_val, silent=True)
                    self.parent.parent.log.info('ISY updated node: ' +
                                                self._id)
            else:
                self.parent.parent.log.warning('ISY could not update node: ' +
                                               self._id)
        elif hint is not None:
            # assume value was set correctly, auto update will correct errors
            self.status.update(hint, silent=True)
            self.parent.parent.log.info('ISY updated node: ' + self._id)

    def off(self):
        """ Turns the node off. """
        response = self.parent.parent.conn.nodeOff(self._id)

        if response is None:
            self.parent.parent.log.warning('ISY could not turn off node: ' +
                                           self._id)
            return False
        else:
            self.parent.parent.log.info('ISY turned off node: ' + self._id)
            self.update(_change2update_interval, hint=0)
            return True

    def on(self, val=None):
        """
        Turns the node on.

        |  [optional] val: The value brightness value (0-255) to set the node to
        """
        response = self.parent.parent.conn.nodeOn(self._id, val)

        if response is None:
            self.parent.parent.log.warning('ISY could not turn on node: ' +
                                           self._id)
            return False
        else:
            if val is None:
                self.parent.parent.log.info('ISY turned on node: ' + self._id)
                val = 255
            else:
                self.parent.parent.log.info('ISY turned on node: ' + self._id +
                                            ', To value: ' + str(val))
                val = int(val)
            self.update(_change2update_interval, hint=val)
            return True

    def fastoff(self):
        """ Turns the node fast off. """
        response = self.parent.parent.conn.nodeFastOff(self._id)

        if response is None:
            self.parent.parent.log.warning('ISY could not fast off node: ' +
                                           self._id)
            return False
        else:
            self.parent.parent.log.info(
                'ISY turned did a fast off with node: ' + self._id)
            self.update(_change2update_interval, hint=0)
            return True

    def faston(self):
        """ Turns the node fast on. """
        response = self.parent.parent.conn.nodeFastOn(self._id)

        if response is None:
            self.parent.parent.log.warning('ISY could not fast on node: ' +
                                           self._id)
            return False
        else:
            self.parent.parent.log.info('ISY did a fast on with node: ' +
                                        self._id)
            self.update(_change2update_interval, hint=255)
            return True

    def bright(self):
        """ Brightens the node by one step. """
        response = self.parent.parent.conn.nodeBright(self._id)

        if response is None:
            self.parent.parent.log.warning('ISY could not brighten node: ' +
                                           self._id)
            return False
        else:
            self.parent.parent.log.info('ISY brightened node: ' + self._id)
            self.update(_change2update_interval)
            return True

    def dim(self):
        """ Dims the node by one step. """
        response = self.parent.parent.conn.nodeDim(self._id)

        if response is None:
            self.parent.parent.log.warning('ISY could not dim node: ' +
                                           self._id)
            return False
        else:
            self.parent.parent.log.info('ISY dimmed node: ' + self._id)
            self.update(_change2update_interval)
            return True

    def _fan_mode(self, mode):
        """ Sends a command to the climate device to set the fan mode. """
        if not hasattr(FAN_MODES, mode):
            self.parent.parent.log.warning('Invalid fan mode: ' + mode)
            return False

        response = self.parent.parent.conn.nodeCliFS(FAN_MODES[mode])

        if response is None:
            self.parent.parent.log.warning('ISY could not send command: ' +
                                           self._id)
            return False
        else:
            self.parent.parent.log.info('ISY command sent: ' + self._id)
            self.update(_change2update_interval)
            return True

    def fan_auto(self):
        """ Sends a command to the climate device to set fan mode=auto. """
        return self._fan_mode(FAN_MODE_AUTO)

    def fan_on(self):
        """ Sends a command to the climate device to set fan mode=on. """
        return self._fan_mode(FAN_MODE_ON)

    def fan_off(self):
        """ Sends a command to the climate device to set fan mode=off.  """
        return self._fan_mode(FAN_MODE_OFF)

    def _climate_mode(self, mode):
        """ Sends a command to the climate device to set the system mode. """
        if not hasattr(CLIMATE_MODES, mode):
            self.parent.parent.log.warning('Invalid climate mode: ' + mode)
            return False

        response = self.parent.parent.nodeCliMD(CLIMATE_MODES[mode])

        if response is None:
            self.parent.parent.log.warning('ISY could not send command: ' +
                                           self._id)
            return False
        else:
            self.parent.parent.log.info('ISY command sent: ' + self._id)
            self.update(_change2update_interval)
            return True

    def climate_off(self):
        """ Sends a command to the device to set the system mode=off. """
        return self._climate_mode(CLIMATE_MODE_OFF)

    def climate_auto(self):
        """ Sends a command to the device to set the system mode=auto. """
        return self._climate_mode(CLIMATE_MODE_AUTO)

    def climate_heat(self):
        """ Sends a command to the device to set the system mode=heat. """
        return self._climate_mode(CLIMATE_MODE_HEAT)

    def climate_cool(self):
        """ Sends a command to the device to set the system mode=cool. """
        return self._climate_mode(CLIMATE_MODE_COOL)

    def climate_setpoint(self, val):
        """ Sends a command to the device to set the system setpoints. """
        # For some reason, wants 2 times the temperature
        for cmd in ['nodeCliSPH', 'nodeCliSPC']:
            response = getattr(self.parent.parent, cmd)(2 * val)

            if response is None:
                self.parent.parent.log.warning('ISY could not send command: ' +
                                               self._id)
                return False

        self.parent.parent.log.info('ISY command sent: ' + self._id)
        self.update(_change2update_interval)
        return True

    def climate_setpoint_heat(self, val):
        """ Sends a command to the device to set the system heat setpoint. """
        # For some reason, wants 2 times the temperature
        response = self.parent.parent.nodeCliSPH(2 * val)

        if response is None:
            self.parent.parent.log.warning('ISY could not send command: ' +
                                           self._id)
            return False
        else:
            self.parent.parent.log.info('ISY command sent: ' + self._id)
            self.update(_change2update_interval)
            return True

    def climate_setpoint_cool(self, val):
        """ Sends a command to the device to set the system cool setpoint. """
        # For some reason, wants 2 times the temperature
        response = self.parent.parent.nodeCliSPC(2 * val)

        if response is None:
            self.parent.parent.log.warning('ISY could not send command: ' +
                                           self._id)
            return False
        else:
            self.parent.parent.log.info('ISY command sent: ' + self._id)
            self.update(_change2update_interval)
            return True

    def lock(self):
        """ Sends a command via secure mode to z-wave locks."""
        response = self.parent.parent.conn.nodeSecMd(self._id, '1')

        if response is None:
            self.parent.parent.log.warning('ISY could not send command: ' +
                                           self._id)
            return False
        else:
            self.parent.parent.log.info('ISY command sent: ' + self._id)
            self.update(_change2update_interval)
            return True

    def unlock(self):
        """ Sends a command via secure mode to z-wave locks."""
        response = self.parent.parent.conn.nodeSecMd(self._id, '0')

        if response is None:
            self.parent.parent.log.warning('ISY could not send command: ' +
                                           self._id)
            return False
        else:
            self.parent.parent.log.info('ISY command sent: ' + self._id)
            self.update(_change2update_interval)
            return True

    def _get_notes(self):
        #if not self._notes:
        self._notes = self.parent.parseNotes(
            self.parent.parent.conn.getNodeNotes(self._id))

    def get_groups(self, controller=True, responder=True):
        """
        Returns the groups (scenes) that this node is a member of.
        If controller is True, then the scene it controls is added to the list
        If responder is True, then the scenes it is a responder of are added to the list
        """
        groups = []
        for child in self.parent.parent.nodes.allLowerNodes:
            if child[0] is 'group':
                if responder:
                    if self._id in self.parent.parent.nodes[child[2]].members:
                        groups.append(child[2])
                elif controller:
                    if self._id in self.parent.parent.nodes[
                            child[2]].controllers:
                        groups.append(child[2])
        return groups

    @property
    def parent_node(self):
        """
        Returns the parent node object of this node. Typically this is for
        devices that are represented as multiple nodes in the ISY, such as
        door and leak sensors. Returns None if there is no parent.
        """
        try:
            return self.parent.getByID(self.parent_nid)
        except:
            return None

    @property
    def spoken(self):
        """Returns the text string of the Spoken property inside the node notes"""
        self._get_notes()
        return self._notes['spoken']
Exemplo n.º 5
0
class Climate(object):

    """
    climate class

    DESCRIPTION:
        This class handles the ISY climate module.

    PARAMETERS:
        Gust_Speed
        Temperature
        Temperature_Rate
        Rain_Rate
        Max_Rain_Rate
        Temperature_High
        Pressure_Rate
        Wind_Speed
        Elevation
        Dew_Point
        Wind_Average_Speed
        Pressure
        Gust_Direction
        Wind_Average_Direction
        Light
        Wind_Direction
        Humidity
        Humidity_Rate
        Rain_Today
        Light_Rate
        Water_Deficit_Yesterday
        Irrigation_Requirement
        Feels_Like
        Temperature_Low
        Evapotranspiration

    ATTRIBUTES:
        parent: The ISY device class
        *_units: Strings of the units used for each of the parameters.
    """

    # Values
    _id2name = ['Temperature', 'Temperature_High', 'Temperature_Low',
                'Feels_Like', 'Temperature_Average', 'Humidity', None,
                'Pressure', None, 'Dew_Point', 'Wind_Speed', None,
                'Wind_Direction', None, 'Gust_Speed', None, 'Total_Rain_Today',
                'Light', None, None, None, 'Evapotranspiration',
                'Irrigation_Requirement', 'Water_Deficit_Yesterday',
                'Elevation', None, None, None, None,
                'Average_Temperature_Tomorrow', 'High_Temperature_Tomorrow',
                'Low_Temperature_Tomorrow', 'Humidity_Tomorrow',
                'Wind_Speed_Tomorrow', 'Gust_Speed_Tomorrow', 'Rain_Tomorrow',
                'Snow_Tomorrow', None, None, None, None,
                'Forecast_Average_Temperature', 'Forecast_High_Temperature',
                'Forecast_Low_Temperature', 'Forecast_Humidity',
                'Forecast_Rain', 'Forecast_Snow', None, None, None, None]

    # value properties
    Temperature = Property(0, readonly=True)
    Temperature_High = Property(0, readonly=True)
    Temperature_Low = Property(0, readonly=True)
    Feels_Like = Property(0, readonly=True)
    Temperature_Average = Property(0, readonly=True)
    Humidity = Property(0, readonly=True)
    Pressure = Property(0, readonly=True)
    Dew_Point = Property(0, readonly=True)
    Wind_Speed = Property(0, readonly=True)
    Wind_Direction = Property(0, readonly=True)
    Gust_Speed = Property(0, readonly=True)
    Total_Rain_Today = Property(0, readonly=True)
    Light = Property(0, readonly=True)
    Evapotranspiration = Property(0, readonly=True)
    Irrigation_Requirement = Property(0, readonly=True)
    Water_Deficit_Yesterday = Property(0, readonly=True)
    Elevation = Property(0, readonly=True)
    # Coverage = Property(0, readonly=True)
    # Intensity = Property(0, readonly=True)
    # Weather_Condition = Property(0, readonly=True)
    # Cloud_Condition = Property(0, readonly=True)
    Average_Temperature_Tomorrow = Property(0, readonly=True)
    High_Temperature_Tomorrow = Property(0, readonly=True)
    Low_Temperature_Tomorrow = Property(0, readonly=True)
    Humidity_Tomorrow = Property(0, readonly=True)
    Wind_Speed_Tomorrow = Property(0, readonly=True)
    Gust_Speed_Tomorrow = Property(0, readonly=True)
    Rain_Tomorrow = Property(0, readonly=True)
    Snow_Tomorrow = Property(0, readonly=True)
    # Coverage_Tomorrow = Property(0, readonly=True)
    # Intensity_Tomorrow = Property(0, readonly=True)
    # Weather_Condition_Tomorrow = Property(0, readonly=True)
    # Cloud_Condition_Tomorrow = Property(0, readonly=True)
    Forecast_Average_Temperature = Property(0, readonly=True)
    Forecast_High_Temperature = Property(0, readonly=True)
    Forecast_Low_Temperature = Property(0, readonly=True)
    Forecast_Humidity = Property(0, readonly=True)
    Forecast_Rain = Property(0, readonly=True)
    Forecast_Snow = Property(0, readonly=True)
    # Forecast_Coverage = Property(0, readonly=True)
    # Forecast_Intensity = Property(0, readonly=True)
    # Forecast_Weather_Condition = Property(0, readonly=True)
    # Forecast_Cloud_Condition = Property(0, readonly=True)

    # unit properties
    Temperature_units = ''
    Temperature_High_units = ''
    Temperature_Low_units = ''
    Feels_Like_units = ''
    Temperature_Average_units = ''
    Humidity_units = ''
    Pressure_units = ''
    Dew_Point_units = ''
    Wind_Speed_units = ''
    Wind_Direction_units = ''
    Gust_Speed_units = ''
    Total_Rain_Today_units = ''
    Light_units = ''
    Evapotranspiration_units = ''
    Irrigation_Requirement_units = ''
    Water_Deficit_Yesterday_units = ''
    Elevation_units = ''
    # Coverage_units = ''
    # Intensity_units = ''
    # Weather_Condition_units = ''
    # Cloud_Condition_units = ''
    Average_Temperature_Tomorrow_units = ''
    High_Temperature_Tomorrow_units = ''
    Low_Temperature_Tomorrow_units = ''
    Humidity_Tomorrow_units = ''
    Wind_Speed_Tomorrow_units = ''
    Gust_Speed_Tomorrow_units = ''
    Rain_Tomorrow_units = ''
    Snow_Tomorrow_units = ''
    # Coverage_Tomorrow_units = ''
    # Intensity_Tomorrow_units = ''
    # Weather_Condition_Tomorrow_units = ''
    # Cloud_Condition_Tomorrow_units = ''
    Forecast_Average_Temperature_units = ''
    Forecast_High_Temperature_units = ''
    Forecast_Low_Temperature_units = ''
    Forecast_Humidity_units = ''
    Forecast_Rain_units = ''
    Forecast_Snow_units = ''
    # Forecast_Coverage_units = ''
    # Forecast_Intensity_units = ''
    # Forecast_Weather_Condition_units = ''
    # Forecast_Cloud_Condition_units = ''

    def __init__(self, parent, xml=None):
        """
        Initiates climate class.

        parent: ISY class
        xml: String of xml data containing the climate data
        """
        super(Climate, self).__init__()
        self.parent = parent
        self.parse(xml)

    def __str__(self):
        return 'Climate Module'

    def __repr__(self):
        out = 'Climate Module\n'
        for attr_name in dir(self):
            attr = getattr(self, attr_name)
            if isinstance(attr, Var):
                units = getattr(self, attr_name + '_units')
                out += '  ' + attr_name + ' = ' + str(attr) \
                    + ' ' + units + '\n'
        return out

    def parse(self, xml):
        """
        Parses the xml data.

        xml: String of the xml data
        """
        try:
            xmldoc = minidom.parseString(xml)
        except:
            self.parent.log.error('ISY Could not parse climate, poorly '
                                  + 'formatted XML.')
        else:
            # parse definitions
            feature = xmldoc.getElementsByTagName('climate')[0]

            for node in feature.childNodes:
                (val, unit) = self._parse_val(node.firstChild.toxml())
                name = node.nodeName
                try:
                    prop = getattr(self, name)
                    prop.update(val, force=True, silent=True)
                    setattr(self, name + '_units', unit)
                except:
                    pass

            self.parent.log.info('ISY Loaded Environment Data')

    def _parse_val(self, val):
        try:
            # assume standard val unit combination
            (val, unit) = self._parse_val_num(val)
        except ValueError:
            # assume direction
            (val, unit) = self._parse_val_dir(val)
        return (val, unit)

    def _parse_val_num(self, val):
        split_val = val.split()
        if len(split_val) == 2:
            return (float(split_val[0]), split_val[1])
        else:
            # probably elevation, assume feet
            return (float(split_val[0]), 'feet')

    def _parse_val_dir(self, val):
        dirs = {'N': 0.,
                'NNE': 22.5,
                'NE': 45.,
                'ENE': 67.5,
                'E': 90.,
                'ESE': 112.5,
                'SE': 135.,
                'SSE': 157.5,
                'S': 180.,
                'SSW': 202.5,
                'SW': 225.,
                'WSW': 247.5,
                'W': 270.,
                'WNW': 292.5,
                'NW': 315.,
                'NNW': 337.5,
                'N/A': None}
        return (dirs[val], 'deg')

    def update(self, waitTime=0):
        """
        Updates the contents of the climate class

        waitTime: [optional] Amount of seconds to wait before updating
        """
        sleep(waitTime)
        xml = self.parent.conn.getClimate()
        self.parse(xml)

    def _upmsg(self, xmldoc):
        cid = int(xmldoc.getElementsByTagName('action')[0]
                  .firstChild.toxml()) - 1
        val_raw = xmldoc.getElementsByTagName('value')[0] \
            .firstChild.toxml().strip()
        unit_raw = xmldoc.getElementsByTagName('unit')[0].firstChild
        if unit_raw is not None:
            unit_raw = unit_raw.toxml().strip()
        else:
            unit_raw = ''

        if cid < len(self._id2name):
            (val, unit) = self._parse_val((val_raw + ' ' + unit_raw).strip())
            cname = self._id2name[cid]
            if cname is not None:
                attr = getattr(self, cname)
                attr.update(val, force=True, silent=True)
                setattr(self, cname + '_units', unit)

                self.parent.log.info('ISY Updated Climate Value: ' + cname)
Exemplo n.º 6
0
class Group(object):
    """
    This class interacts with ISY groups (scenes).

    |  parent: The node manager object.
    |  nid: The node ID.
    |  name: The node name.
    |  members: List of the members in this group.
    |  controllers: List of the controllers in this group.
    |  spoken: The string of the Notes Spoken field.

    :ivar dimmable: Boolean value idicating that this group cannot be dimmed.
    :ivar hasChildren: Boolean value indicating that this group has no children.
    :ivar members: List of the members of this group.
    :ivar controllers: List of the controllers of this group.
    :ivar name: The name of this group.
    :ivar status: Watched property indicating the status of the group.
    """

    status = Property(0)
    hasChildren = False

    def __init__(self,
                 parent,
                 nid,
                 name,
                 members=None,
                 controllers=None,
                 notes=False):
        self.parent = parent
        self._id = nid
        self.name = name
        self._members = members or []
        self._controllers = controllers or []
        self.dimmable = False
        self._running = False
        self._notes = notes

        # listen for changes in children
        self._membersHandlers = [
            self.parent[m].status.subscribe('changed', self.update)
            for m in self.members
        ]

        # get and update the status
        self.update()

        # respond to non-silent changes in status
        self.status.reporter = self.__report_status__

    def __del__(self):
        """ Cleanup event handlers before deleting. """
        for handler in self._membersHandlers:
            handler.unsubscribe()

    def __str__(self):
        """ Return a string representation for this group. """
        return 'Group(' + self._id + ')'

    def __report_status__(self, new_val):
        # first clean the status input
        if self.status > 0:
            clean_status = 255
        elif self.status <= 0:
            clean_status = 0
        if self.status != clean_status:
            self.status.update(clean_status, force=True, silent=True)

        # now update the nodes
        if clean_status > 0:
            self.on()
        else:
            self.off()

    @property
    def members(self):
        return self._members

    @property
    def controllers(self):
        return self._controllers

    def update(self, e=None):
        """ Update the group with values from the controller. """
        for m in self.members:
            if self.parent[m].status == None:
                continue
            elif self.parent[m].status > 0:
                self.status.update(255, force=True, silent=True)
                return
        self.status.update(0, force=True, silent=True)

    def off(self):
        """Turns off all the nodes in a scene."""
        response = self.parent.parent.conn.nodeOff(self._id)

        if response is None:
            self.parent.parent.log.warning('ISY could not turn off scene: ' +
                                           self._id)
            return False
        else:
            self.parent.parent.log.info('ISY turned off scene: ' + self._id)
            return True

    def on(self):
        """Turns on all the nodes in the scene to the set values."""
        response = self.parent.parent.conn.nodeOn(self._id, None)

        if response is None:
            self.parent.parent.log.warning('ISY could not turn on scene: ' +
                                           self._id)
            return False
        else:
            self.parent.parent.log.info('ISY turned on scene: ' + self._id)
            return True

    def _get_notes(self):
        #if not self._notes:
        self._notes = self.parent.parseNotes(
            self.parent.parent.conn.getNodeNotes(self._id))

    @property
    def spoken(self):
        """Returns the text string of the Spoken property inside the group notes"""
        self._get_notes()
        return self._notes['spoken']
Exemplo n.º 7
0
class Program(Folder):

    lastUpdate = Property(_empty_time, readonly=True)
    lastRun = Property(_empty_time, readonly=True)
    lastFinished = Property(_empty_time, readonly=True)
    enabled = Property(True)
    runAtStartup = Property(True)
    running = Property(False, readonly=True)
    ranThen = Property(0, readonly=True)
    ranElse = Property(0, readonly=True)
    dtype = 'program'

    def __init__(self, parent, pid, pname, pstatus, plastup, plastrun, plastfin,
                 penabled, pstartrun, prunning):
        super(Program, self).__init__(parent, pid, pname, pstatus)
        self.lastUpdate.update(plastup, force=True, silent=True)
        self.lastRun.update(plastrun, force=True, silent=True)
        self.lastFinished.update(plastfin, force=True, silent=True)
        self.enabled.update(penabled, force=True, silent=True)
        self.enabled.responder = self.__report_enabled__
        self.runAtStartup.update(pstartrun, force=True, silent=True)
        self.runAtStartup.responder = self.__report_startrun__
        self.running.update(prunning, force=True, silent=True)

    def __str__(self):
        return 'Program(' + self._id + ')'

    def __report_enabled__(self, val):
        self.noupdate = True
        fun = self.enable if val else self.disable
        fun()
        self.noupdate = False

    def __report_startrun__(self, val):
        self.noupdate = True
        fun = self.enableRunAtStartup if val else self.disableRunAtStartup
        fun()
        self.noupdate = False

    def update(self, waitTime=0, data=None):
        if not self.noupdate:
            if data is not None:
                prunning = (data['plastrun'] >= data['plastup']) or \
                    data['prunning']
                self.status.update(data['pstatus'], force=True, silent=True)
                self.lastUpdate.update(data['plastup'],
                                       force=True, silent=True)
                self.lastRun.update(data['plastrun'], force=True, silent=True)
                self.lastFinished.update(data['plastfin'],
                                         force=True, silent=True)
                self.enabled.update(data['penabled'], force=True, silent=True)
                self.runAtStartup.update(data['pstartrun'],
                                         force=True, silent=True)
                self.running.update(prunning, force=True, silent=True)
            else:
                self.parent.update(waitTime, pid=self._id)

    def enable(self):
        response = self.parent.parent.conn.programEnable(self._id)

        if response is None:
            self.parent.parent.log.warning('ISY could not enable program: '
                                           + self._id)
        else:
            self.parent.parent.log.info('ISY enabled program: ' + self._id)
            self.update(_change2update_interval)

    def disable(self):
        response = self.parent.parent.conn.programDisable(self._id)

        if response is None:
            self.parent.parent.log.warning('ISY could not disable program: '
                                           + self._id)
        else:
            self.parent.parent.log.info('ISY disabled program: ' + self._id)
            self.update(_change2update_interval)

    def enableRunAtStartup(self):
        response = self.parent.parent.conn.programEnableRunAtStartup(self._id)

        if response is None:
            self.parent.parent.log.warning('ISY could not enable run at '
                                           + 'startup for program: '
                                           + self._id)
        else:
            self.parent.parent.log.info('ISY enabled run at startup for '
                                        + 'program: ' + self._id)
            self.update(_change2update_interval)

    def disableRunAtStartup(self):
        response = self.parent.parent.conn.programDisableRunAtStartup(self._id)

        if response is None:
            self.parent.parent.log.warning('ISY could not disable run at '
                                           + 'startup for program: '
                                           + self._id)
        else:
            self.parent.parent.log.info('ISY disabled run at startup for '
                                        + 'program: ' + self._id)
            self.update(_change2update_interval)
Exemplo n.º 8
0
class Folder(object):

    status = Property(0, readonly=True)
    dtype = 'folder'

    def __init__(self, parent, pid, pname, pstatus):
        self.noupdate = False
        self.parent = parent
        self.name = pname
        self._id = pid

        self.status.update(pstatus, force=True, silent=True)

    def __str__(self):
        return 'Folder(' + self._id + ')'

    @property
    def leaf(self):
        ''' returns the object at the current level of navigation. '''
        return self

    def update(self, waitTime=0, data=None):
        if not self.noupdate:
            if data is not None:
                self.status.update(data['pstatus'], force=True, silent=True)
            else:
                self.parent.update(waitTime, pid=self._id)

    def run(self):
        response = self.parent.parent.conn.programRun(self._id)

        if response is None:
            self.parent.parent.log.warning('ISY could not run program: ' +
                                           self._id)
        else:
            self.parent.parent.log.info('ISY ran program: ' + self._id)
            self.update(_change2update_interval)

    def runThen(self):
        response = self.parent.parent.conn.programRunThen(self._id)

        if response is None:
            self.parent.parent.log.warning(
                "ISY couldn't run then in program: " + self._id)
        else:
            self.parent.parent.log.info('ISY ran then in program: ' + self._id)
            self.update(_change2update_interval)

    def runElse(self):
        response = self.parent.parent.conn.programRunElse(self._id)

        if response is None:
            self.parent.parent.log.warning(
                "ISY couldn't run else in program: " + self._id)
        else:
            self.parent.parent.log.info('ISY ran else in program: ' + self._id)
            self.update(_change2update_interval)

    def stop(self):
        response = self.parent.parent.conn.programStop(self._id)

        if response is None:
            self.parent.parent.log.warning('ISY could not stop program: ' +
                                           self._id)
        else:
            self.parent.parent.log.info('ISY stopped program: ' + self._id)
            self.update(_change2update_interval)
Exemplo n.º 9
0
Arquivo: node.py Projeto: ssnui/PyISY
class Node(object):
    """
    Node class

    DESCRIPTION:
        This class handles ISY nodes.

    ATTRIBUTES:
        parent: The nodes class
        status: The status of the node
    """

    status = Property(0)
    hasChildren = False

    def __init__(self, parent, nid, nval, name, dimmable=True):
        self.parent = parent
        self._id = nid
        self.dimmable = dimmable
        self.name = name

        self.status = nval
        self.status.reporter = self.__report_status__

    def __str__(self):
        return 'Node(' + self._id + ')'

    def __report_status__(self, new_val):
        self.on(new_val)

    def update(self, waitTime=0, hint=None):
        if not self.parent.parent.auto_update:
            sleep(waitTime)
            xml = self.parent.parent.conn.updateNode(self._id)

            if xml is not None:
                try:
                    xmldoc = minidom.parseString(xml)
                except:
                    self.parent.parent.log.error('ISY Could not parse nodes,' +
                                                 'poorly formatted XML.')
                else:
                    new_st = int(
                        xmldoc.getElementsByTagName('property')
                        [0].attributes['value'].value)
                    self.status.update(new_st, silent=True)
                    self.parent.parent.log.info('ISY updated node: ' +
                                                self._id)
            else:
                self.parent.parent.log.warning('ISY could not update node: ' +
                                               self._id)
        elif hint is not None:
            # assume value was set correctly, auto update will correct errors
            self.status.update(hint, silent=True)
            self.parent.parent.log.info('ISY updated node: ' + self._id)

    def off(self):
        response = self.parent.parent.conn.nodeOff(self._id)

        if response is None:
            self.parent.parent.log.warning('ISY could not turn off node: ' +
                                           self._id)
        else:
            self.parent.parent.log.info('ISY turned off node: ' + self._id)
            self.update(_change2update_interval, hint=0)

    def on(self, val=None):
        response = self.parent.parent.conn.nodeOn(self._id, val)

        if response is None:
            self.parent.parent.log.warning('ISY could not turn on node: ' +
                                           self._id)
        else:
            if val is None:
                self.parent.parent.log.info('ISY turned on node: ' + self._id)
                val = 255
            else:
                self.parent.parent.log.info('ISY turned on node: ' + self._id +
                                            ', To value: ' + str(val))
                val = int(val)
            self.update(_change2update_interval, hint=val)

    def fastoff(self):
        response = self.parent.parent.conn.nodeFastOff(self._id)

        if response is None:
            self.parent.parent.log.warning('ISY could not fast off node: ' +
                                           self._id)
        else:
            self.parent.parent.log.info(
                'ISY turned did a fast off with node: ' + self._id)
            self.update(_change2update_interval, hint=0)

    def faston(self):
        response = self.parent.parent.conn.nodeFastOn(self._id)

        if response is None:
            self.parent.parent.log.warning('ISY could not fast on node: ' +
                                           self._id)
        else:
            self.parent.parent.log.info('ISY did a fast on with node: ' +
                                        self._id)
            self.update(_change2update_interval, hint=255)

    def bright(self):
        response = self.parent.parent.conn.nodeBright(self._id)

        if response is None:
            self.parent.parent.log.warning('ISY could not brighten node: ' +
                                           self._id)
        else:
            self.parent.parent.log.info('ISY brightened node: ' + self._id)
            self.update(_change2update_interval)

    def dim(self):
        response = self.parent.parent.conn.nodeDim(self._id)

        if response is None:
            self.parent.parent.log.warning('ISY could not dim node: ' +
                                           self._id)
        else:
            self.parent.parent.log.info('ISY dimmed node: ' + self._id)
            self.update(_change2update_interval)
Exemplo n.º 10
0
class Climate(object):

    """
    This class handles the ISY climate module.

    |  parent: ISY class
    |  xml: String of xml data containing the climate data

    :ivar Gust_Speed: Watched Variable representing the gust speed.
    :ivar Temperature: Watched Variable representing the temperature.
    :ivar Temperature_Rate: Watched Variable representing the temperature rate.
    :ivar Rain_Rate: Watched Variable representing the rain rate.
    :ivar Max_Rain_Rate: Watched Variable representing the rain rate.
    :ivar Temperature_High: Watched Variable representing the high temperature.
    :ivar Pressure_Rate: Watched variable representing the pressure rate.
    :ivar Wind_Speed: Watched Variable representing the wind speed.
    :ivar Elevation: Watched Variable representing the elevation.
    :ivar Dew_Point: Watched Variable representing the dew point.
    :ivar Wind_Average_Speed: Watched Variable representing the avg wind speed.
    :ivar Pressure: Watched Variable representing the pressure.
    :ivar Gust_Direction: Watched Variable representing the gust direction.
    :ivar Wind_Average_Direction: Watched Variable representing the average wind
                                  direction.
    :ivar Light: Watched Variable representing the amount of light.
    :ivar Wind_Direction: Watched Variable representing the wind direction.
    :ivar Humidity: Watched Variable representing the humidity.
    :ivar Humidity_Rate: Watched Variable representing the humidity rate.
    :ivar Rain_Today: Watched Variable representing the forcast rain today.
    :ivar Light_Rate: Watched Variable representing the light rate.
    :ivar Water_Deficit_Yesterday: Watched Variable representing the water
                                   deficit yesterday.
    :ivar Irrigation_Requirement: Watched Variable representing the irrigation
                                  requirement.
    :ivar Feels_Like: Watched Variable representing the feels like temperature.
    :ivar Temperature_Low: Watched Variable representing the low temperature.
    :ivar Evapotranspiration: Watched Variable representing the
                              evapotranspiration amount.
    :ivar Gust_Speed_units: Gust speed units.
    :ivar Temperature_units: Temperature units.
    :ivar Temperature_Rate_units: Temperature rate units.
    :ivar Rain_Rate_units: Rain rate units.
    :ivar Max_Rain_Rate_units: Max rain rate units.
    :ivar Temperature_High_units: High temperature units.
    :ivar Pressure_Rate_units: Pressure rate units.
    :ivar Wind_Speed_units: Wind speed units.
    :ivar Elevation_units: Elevation units.
    :ivar Dew_Point_units: Dew point units.
    :ivar Wind_Average_Speed_units: Average wind speed units.
    :ivar Pressure_units: Pressure units.
    :ivar Gust_Direction_units: Gust direction units.
    :ivar Wind_Average_Direction_units: Average wind direction units.
    :ivar Light_units: Light amount units.
    :ivar Wind_Direction_units: Wind direction units.
    :ivar Humidity_units: Humidity units.
    :ivar Humidity_Rate_units: Humidity rate units.
    :ivar Rain_Today_units: Rain forecast units.
    :ivar Light_Rate_units: Light rate units.
    :ivar Water_Deficit_Yesterday_units: Water deficit units.
    :ivar Irrigation_Requirement_units: Irrigation requirement units.
    :ivar Feels_Like_units: Feels like temperature units.
    :ivar Temperature_Low_units: Low temperature units.
    :ivar Evapotranspiration_units: Evapotranspiration units.
    """

    # Values
    _id2name = ['Temperature', 'Temperature_High', 'Temperature_Low',
                'Feels_Like', 'Temperature_Average', 'Humidity', None,
                'Pressure', None, 'Dew_Point', 'Wind_Speed', None,
                'Wind_Direction', None, 'Gust_Speed', None, 'Total_Rain_Today',
                'Light', None, None, None, 'Evapotranspiration',
                'Irrigation_Requirement', 'Water_Deficit_Yesterday',
                'Elevation', None, None, None, None,
                'Average_Temperature_Tomorrow', 'High_Temperature_Tomorrow',
                'Low_Temperature_Tomorrow', 'Humidity_Tomorrow',
                'Wind_Speed_Tomorrow', 'Gust_Speed_Tomorrow', 'Rain_Tomorrow',
                'Snow_Tomorrow', None, None, None, None,
                'Forecast_Average_Temperature', 'Forecast_High_Temperature',
                'Forecast_Low_Temperature', 'Forecast_Humidity',
                'Forecast_Rain', 'Forecast_Snow', None, None, None, None]

    # value properties
    Temperature = Property(0, readonly=True)
    Temperature_High = Property(0, readonly=True)
    Temperature_Low = Property(0, readonly=True)
    Feels_Like = Property(0, readonly=True)
    Temperature_Average = Property(0, readonly=True)
    Humidity = Property(0, readonly=True)
    Pressure = Property(0, readonly=True)
    Dew_Point = Property(0, readonly=True)
    Wind_Speed = Property(0, readonly=True)
    Wind_Direction = Property(0, readonly=True)
    Gust_Speed = Property(0, readonly=True)
    Total_Rain_Today = Property(0, readonly=True)
    Light = Property(0, readonly=True)
    Evapotranspiration = Property(0, readonly=True)
    Irrigation_Requirement = Property(0, readonly=True)
    Water_Deficit_Yesterday = Property(0, readonly=True)
    Elevation = Property(0, readonly=True)
    # Coverage = Property(0, readonly=True)
    # Intensity = Property(0, readonly=True)
    # Weather_Condition = Property(0, readonly=True)
    # Cloud_Condition = Property(0, readonly=True)
    Average_Temperature_Tomorrow = Property(0, readonly=True)
    High_Temperature_Tomorrow = Property(0, readonly=True)
    Low_Temperature_Tomorrow = Property(0, readonly=True)
    Humidity_Tomorrow = Property(0, readonly=True)
    Wind_Speed_Tomorrow = Property(0, readonly=True)
    Gust_Speed_Tomorrow = Property(0, readonly=True)
    Rain_Tomorrow = Property(0, readonly=True)
    Snow_Tomorrow = Property(0, readonly=True)
    # Coverage_Tomorrow = Property(0, readonly=True)
    # Intensity_Tomorrow = Property(0, readonly=True)
    # Weather_Condition_Tomorrow = Property(0, readonly=True)
    # Cloud_Condition_Tomorrow = Property(0, readonly=True)
    Forecast_Average_Temperature = Property(0, readonly=True)
    Forecast_High_Temperature = Property(0, readonly=True)
    Forecast_Low_Temperature = Property(0, readonly=True)
    Forecast_Humidity = Property(0, readonly=True)
    Forecast_Rain = Property(0, readonly=True)
    Forecast_Snow = Property(0, readonly=True)
    # Forecast_Coverage = Property(0, readonly=True)
    # Forecast_Intensity = Property(0, readonly=True)
    # Forecast_Weather_Condition = Property(0, readonly=True)
    # Forecast_Cloud_Condition = Property(0, readonly=True)

    # unit properties
    Temperature_units = ''
    Temperature_High_units = ''
    Temperature_Low_units = ''
    Feels_Like_units = ''
    Temperature_Average_units = ''
    Humidity_units = ''
    Pressure_units = ''
    Dew_Point_units = ''
    Wind_Speed_units = ''
    Wind_Direction_units = ''
    Gust_Speed_units = ''
    Total_Rain_Today_units = ''
    Light_units = ''
    Evapotranspiration_units = ''
    Irrigation_Requirement_units = ''
    Water_Deficit_Yesterday_units = ''
    Elevation_units = ''
    # Coverage_units = ''
    # Intensity_units = ''
    # Weather_Condition_units = ''
    # Cloud_Condition_units = ''
    Average_Temperature_Tomorrow_units = ''
    High_Temperature_Tomorrow_units = ''
    Low_Temperature_Tomorrow_units = ''
    Humidity_Tomorrow_units = ''
    Wind_Speed_Tomorrow_units = ''
    Gust_Speed_Tomorrow_units = ''
    Rain_Tomorrow_units = ''
    Snow_Tomorrow_units = ''
    # Coverage_Tomorrow_units = ''
    # Intensity_Tomorrow_units = ''
    # Weather_Condition_Tomorrow_units = ''
    # Cloud_Condition_Tomorrow_units = ''
    Forecast_Average_Temperature_units = ''
    Forecast_High_Temperature_units = ''
    Forecast_Low_Temperature_units = ''
    Forecast_Humidity_units = ''
    Forecast_Rain_units = ''
    Forecast_Snow_units = ''
    # Forecast_Coverage_units = ''
    # Forecast_Intensity_units = ''
    # Forecast_Weather_Condition_units = ''
    # Forecast_Cloud_Condition_units = ''

    def __init__(self, parent, xml=None):
        super(Climate, self).__init__()
        self.parent = parent
        self.parse(xml)

    def __str__(self):
        """ Returns a string representing the climate manager. """
        return 'Climate Module'

    def __repr__(self):
        """ Returns a long string showing all the climate values. """
        out = 'Climate Module\n'
        for attr_name in dir(self):
            attr = getattr(self, attr_name)
            if isinstance(attr, Var):
                units = getattr(self, attr_name + '_units')
                out += '  ' + attr_name + ' = ' + str(attr) \
                    + ' ' + units + '\n'
        return out

    def parse(self, xml):
        """
        Parses the xml data.

        xml: String of the xml data
        """
        try:
            xmldoc = minidom.parseString(xml)
        except:
            self.parent.log.error('ISY Could not parse climate, poorly '
                                  + 'formatted XML.')
        else:
            # parse definitions
            feature = xmldoc.getElementsByTagName('climate')[0]

            for node in feature.childNodes:
                (val, unit) = self._parse_val(node.firstChild.toxml())
                name = node.nodeName
                try:
                    prop = getattr(self, name)
                    prop.update(val, force=True, silent=True)
                    setattr(self, name + '_units', unit)
                except:
                    pass

            self.parent.log.info('ISY Loaded Environment Data')

    def _parse_val(self, val):
        try:
            # assume standard val unit combination
            (val, unit) = self._parse_val_num(val)
        except ValueError:
            # assume direction
            (val, unit) = self._parse_val_dir(val)
        return (val, unit)

    def _parse_val_num(self, val):
        split_val = val.split()
        if len(split_val) == 2:
            return (float(split_val[0]), split_val[1])
        else:
            # probably elevation, assume feet
            return (float(split_val[0]), 'feet')

    def _parse_val_dir(self, val):
        dirs = {'N': 0.,
                'NNE': 22.5,
                'NE': 45.,
                'ENE': 67.5,
                'E': 90.,
                'ESE': 112.5,
                'SE': 135.,
                'SSE': 157.5,
                'S': 180.,
                'SSW': 202.5,
                'SW': 225.,
                'WSW': 247.5,
                'W': 270.,
                'WNW': 292.5,
                'NW': 315.,
                'NNW': 337.5,
                'N/A': None}
        return (dirs[val], 'deg')

    def update(self, waitTime=0):
        """
        Updates the contents of the climate class

        waitTime: [optional] Amount of seconds to wait before updating
        """
        sleep(waitTime)
        xml = self.parent.conn.getClimate()
        self.parse(xml)

    def _upmsg(self, xmldoc):
        cid = int(xmldoc.getElementsByTagName('action')[0]
                  .firstChild.toxml()) - 1
        val_raw = xmldoc.getElementsByTagName('value')[0] \
            .firstChild.toxml().strip()
        unit_raw = xmldoc.getElementsByTagName('unit')[0].firstChild
        if unit_raw is not None:
            unit_raw = unit_raw.toxml().strip()
        else:
            unit_raw = ''

        if cid < len(self._id2name):
            (val, unit) = self._parse_val((val_raw + ' ' + unit_raw).strip())
            cname = self._id2name[cid]
            if cname is not None:
                attr = getattr(self, cname)
                attr.update(val, force=True, silent=True)
                setattr(self, cname + '_units', unit)

                self.parent.log.info('ISY Updated Climate Value: ' + cname)