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
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)
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)
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']
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)
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']
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)
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)
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)
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)