def getTheSensor(lbl, never_assume_dead=False, getHighest=False, getLowest=False): # Each sensor entry in the configuration file can be a a single item name or a python list where you can # define multiple sensor names. The first sensor in that list that has reported within the value set in # sensor_dead_after_mins will be used. (Unless never_assume_dead is set to True) # When "getHighest" argument is set to True, the sensor name with the highest value is picked. # When "getlowest" argument is set to True, the sensor name with the lowest value is picked. def isSensorAlive(sName): if getLastUpdate(PersistenceExtensions, ir.getItem(sName)).isAfter( DateTime.now().minusMinutes( sensor_dead_after_mins)): return True else: self.log.warning('Sensor device ' + unicode(sName) + ' has not reported since: ' + str( getLastUpdate(PersistenceExtensions, ir.getItem(sName)))) return False sensorName = None if lbl in self.config.wunderground[ 'sensors'] and self.config.wunderground['sensors'][ lbl] is not None: tSens = self.config.wunderground['sensors'][lbl] if isinstance(tSens, list): _highestValue = 0 _lowestValue = 999999999 for s in tSens: if s is None: break # Get the first sensor that is not dead and find the sensor with the highest or the lowest value if requested if never_assume_dead or isSensorAlive(s): if getHighest: _itemValue = getItemValue(s, 0) if _itemValue > _highestValue: _highestValue = _itemValue sensorName = s elif getLowest: _itemValue = getItemValue(s, 0) if _itemValue < _lowestValue: _lowestValue = _itemValue sensorName = s else: sensorName = s break else: if never_assume_dead or isSensorAlive(tSens): sensorName = tSens if sensorName is not None: self.log.debug("Device used for " + unicode(lbl) + ": " + sensorName) return sensorName
def __init__(self, parent, zoneNumber, cfg): ''' Initialise the IdeAlarmZone class Expects: - Parent object - zoneNumber (integer) The zone's ordinal number - cfg (dictionary) The zone's configuration dictionary ''' self._armingMode = None self._zoneStatus = None self.zoneNumber = zoneNumber self.alertDevices = cfg['alertDevices'] self.name = cfg['name'] self.armAwayToggleSwitch = cfg['armAwayToggleSwitch'] self.armHomeToggleSwitch = cfg['armHomeToggleSwitch'] self.mainZone = cfg['mainZone'] self.canArmWithTrippedSensors = cfg['canArmWithTrippedSensors'] self.alarmTestMode = parent.alarmTestMode self.parent = weakref.ref(parent) # <= garbage-collector safe! self.log = logging.getLogger(LOG_PREFIX + '.IdeAlarmZone.' + self.name.decode('utf8')) self.sensors = [] for sensor in cfg['sensors']: self.sensors.append(IdeAlarmSensor(self, sensor)) self.armingModeItem = cfg['armingModeItem'] self.statusItem = cfg['statusItem'] self.openSections = self.countOpenSections() self.setArmingMode( getItemValue(self.armingModeItem, ARMINGMODE['DISARMED']) ) # Will also set the zone status to normal self.log.info('ideAlarm Zone ' + self.name.decode('utf8') + ' initialized with ' + str(self.openSections) + ' open sensors')
def onSensorChange(self, sensor): ''' Called whenever an enabled sensor has tripped ON or OPEN ''' if self.getArmingMode() not in [ARMINGMODE['ARMED_HOME'], ARMINGMODE['ARMED_AWAY']] \ or self.getZoneStatus() not in [ZONESTATUS['NORMAL']] \ or (self.getArmingMode() == ARMINGMODE['ARMED_HOME'] and sensor.sensorClass == 'B') \ or getItemValue('Z'+str(self.zoneNumber)+'_Exit_Timer', OFF) == ON: self.log.info( sensor.name.decode('utf-8') + ' was tripped, but we are ignoring it') return if (not sensor.armWarn): self.setZoneStatus(ZONESTATUS['TRIPPED']) self.log.info( sensor.name.decode('utf-8') + ' was tripped, starting entry timer') postUpdateCheckFirst('Z' + str(self.zoneNumber) + '_Entry_Timer', ON) else: self.onEntryTimer() self.log.info( sensor.name.decode('utf-8') + ' was tripped, armWarn is set to True, go directly to the ALERT Mode' )
def playsound(fileName, ttsPrio=PRIO['MODERATE'], **keywords): ''' Play a sound mp3 file function. First argument is positional and mandatory. Remaining arguments are optionally keyword arguments. Example: playsound("Hello.mp3") Example: playsound("Hello.mp3", PRIO['HIGH'], room='Kitchen', volume=42) @param param1: Sound file name to play (positional argument) (files need to be put in the folder conf/sounds) @param param2: Priority as defined by PRIO. Defaults to PRIO['MODERATE'] @param room: Room to play in. Defaults to "All". @return: this is a description of what is returned ''' module_name = 'playsound' log = logging.getLogger(LOG_PREFIX+'.'+module_name) log.setLevel(logging.INFO) def getDefaultRoom(): # Search for the default room to speak in for the_key, the_value in config.sonos['rooms'].iteritems(): if the_value['defaultttsdevice']: return the_key return 'All' if ((getItemValue(config.customItemNames['allowTTSSwitch'], ON) != ON) and (ttsPrio <= PRIO['MODERATE'])): log.info(unicode(config.customItemNames['allowTTSSwitch']) + " is OFF and ttsPrio is to low to play sound \'" + fileName + "\' at this moment.") return False room = getDefaultRoom() if 'room' not in keywords else keywords['room'] rooms = [] if room == 'All' or room is None: for the_key, the_value in config.sonos['rooms'].iteritems(): rooms.append(config.sonos['rooms'][the_key]) log.debug('Room found: ' + config.sonos['rooms'][the_key]['name']) else: sonosSpeaker = config.sonos['rooms'].get(room, None) if sonosSpeaker is None: log.error("Room "+room+" wasn't found in the sonos rooms dictionary") return rooms.append(sonosSpeaker) log.debug('Room found: ' + sonosSpeaker['name']) for aRoom in rooms: ttsVol = None if 'ttsVol' not in keywords else keywords['ttsVol'] if not ttsVol or ttsVol >= 70: if ttsPrio == PRIO['LOW']: ttsVol = 30 elif ttsPrio == PRIO['MODERATE']: ttsVol = 40 elif ttsPrio == PRIO['HIGH']: ttsVol = 60 elif ttsPrio == PRIO['EMERGENCY']: ttsVol = 70 else: ttsVol = aRoom['ttsvolume'] Audio.playSound(aRoom['audiosink'], fileName) log.info("playSound: \'" + fileName + "\'" + ' in room: \'' + aRoom['name'] + '\' at volume: \'' + str(ttsVol) + '\'.') return True
def execute(self, modules, inputs): SCRIPT_VERSION = '2.3.1' WU_URL = "http://weatherstation.wunderground.com/weatherstation/updateweatherstation.php" def ms_to_mph(input_speed): # convert input_speed from meter per second to miles per hour return round(input_speed / 0.44704, 2) def mm_to_inch(mm): # convert mm to inches return round(mm / 25.4, 2) def mbar_to_inches_mercury(input_pressure): # convert mbar to inches mercury return round(input_pressure * 0.02953, 2) def lux_to_watts_m2(lux): # Convert lux [lx] to watt/m² (at 555 nm) # Should typically be around 800-900 watt/m² at mid summer full sun diation at 13.00 h # return int(round(float(lux) * 0.01464128843)) return int(round(float(lux) * 0.015454545)) def getTheSensor(lbl, never_assume_dead=False, getHighest=False, getLowest=False): # Each sensor entry in the configuration file can be a a single item name or a python list where you can # define multiple sensor names. The first sensor in that list that has reported within the value set in # sensor_dead_after_mins will be used. (Unless never_assume_dead is set to True) # When "getHighest" argument is set to True, the sensor name with the highest value is picked. # When "getlowest" argument is set to True, the sensor name with the lowest value is picked. def isSensorAlive(sName): if getLastUpdate(PersistenceExtensions, ir.getItem(sName)).isAfter( DateTime.now().minusMinutes( sensor_dead_after_mins)): return True else: self.log.warning('Sensor device ' + unicode(sName) + ' has not reported since: ' + str( getLastUpdate(PersistenceExtensions, ir.getItem(sName)))) return False sensorName = None if lbl in self.config.wunderground[ 'sensors'] and self.config.wunderground['sensors'][ lbl] is not None: tSens = self.config.wunderground['sensors'][lbl] if isinstance(tSens, list): _highestValue = 0 _lowestValue = 999999999 for s in tSens: if s is None: break # Get the first sensor that is not dead and find the sensor with the highest or the lowest value if requested if never_assume_dead or isSensorAlive(s): if getHighest: _itemValue = getItemValue(s, 0) if _itemValue > _highestValue: _highestValue = _itemValue sensorName = s elif getLowest: _itemValue = getItemValue(s, 0) if _itemValue < _lowestValue: _lowestValue = _itemValue sensorName = s else: sensorName = s break else: if never_assume_dead or isSensorAlive(tSens): sensorName = tSens if sensorName is not None: self.log.debug("Device used for " + unicode(lbl) + ": " + sensorName) return sensorName self.log.setLevel(self.config.wunderground['logLevel']) global wu_loop_count sensor_dead_after_mins = self.config.wunderground[ 'sensor_dead_after_mins'] # The time after which a sensor is presumed to be dead if (not self.config.wunderground['stationdata']['weather_upload']) \ or (self.config.wunderground['stationdata']['weather_upload'] and wu_loop_count%self.config.wunderground['stationdata']['upload_frequency'] == 0): if self.config.wunderground['stationdata']['weather_upload']: self.log.debug('Uploading data to Weather Underground') else: self.log.debug( 'No data to will be upladed to Weather Underground') sdf = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss") dateutc = sdf.print(DateTime.now((DateTimeZone.UTC))) tempf = None temp = None sensorName = getTheSensor('tempc', getLowest=True) if sensorName is not None: temp = Temp( getItemValue(sensorName, 0.0), 'c' ) # Outdoor temp, c - celsius, f - fahrenheit, k - kelvin tempf = str(round(temp.f, 1)) soiltempf = None sensorName = getTheSensor('soiltempc') if sensorName is not None: _temp = Temp( getItemValue(sensorName, 0.0), 'c') # Soil temp, c - celsius, f - fahrenheit, k - kelvin soiltempf = str(round(_temp.f, 1)) humidity = None sensorName = getTheSensor('humidity') if sensorName is not None: humidity = getItemValue(sensorName, 0.0) dewptf = None heatidxf = None if humidity is not None and temp is not None: dewptf = str( round(dew_point(temperature=temp, humidity=humidity).f, 1)) # calculate Dew Point heatidxf = str( round( heat_index(temperature=temp, humidity=humidity).f, 1)) # calculate Heat Index pressure = None sensorName = getTheSensor('pressurembar') if sensorName is not None: _mbar = getItemValue(sensorName, 0) if ((_mbar < 1070) and (_mbar > 920)): pressure = str(mbar_to_inches_mercury(_mbar)) rainin = None sensorName = getTheSensor('rainhour', never_assume_dead=True) if sensorName is not None: rainin = str(mm_to_inch(getItemValue(sensorName, 0.0))) dailyrainin = None sensorName = getTheSensor('raintoday', never_assume_dead=True) if sensorName is not None: dailyrainin = str(mm_to_inch(getItemValue(sensorName, 0.0))) soilmoisture = None sensorName = getTheSensor('soilmoisture') if sensorName is not None: soilmoisture = str( int(round(getItemValue(sensorName, 0.0) * 100 / 1023))) winddir = None sensorName = getTheSensor('winddir') if sensorName is not None: winddir = str(getItemValue(sensorName, 0)) windspeedmph = None sensorName = getTheSensor('windspeedms') if sensorName is not None: windspeedmph = str(ms_to_mph(getItemValue(sensorName, 0.0))) windgustmph = None sensorName = getTheSensor('windgustms') if sensorName is not None: windgustmph = str(ms_to_mph(getItemValue(sensorName, 0.0))) windgustdir = None sensorName = getTheSensor('windgustdir') if sensorName is not None: windgustdir = str(getItemValue(sensorName, 0)) windspdmph_avg2m = None sensorName = getTheSensor('windspeedms_avg2m') if sensorName is not None: windspdmph_avg2m = str(ms_to_mph(getItemValue(sensorName, 0.0))) winddir_avg2m = None sensorName = getTheSensor('winddir_avg2m') if sensorName is not None: winddir_avg2m = str(getItemValue(sensorName, 0)) windgustmph_10m = None sensorName = getTheSensor('windgustms_10m') if sensorName is not None: windgustmph_10m = str(ms_to_mph(getItemValue(sensorName, 0.0))) windgustdir_10m = None sensorName = getTheSensor('windgustdir_10m') if sensorName is not None: windgustdir_10m = str(getItemValue(sensorName, 0)) solarradiation = None sensorName = getTheSensor('solarradiation', getHighest=True) if sensorName is not None: solarradiation = str( lux_to_watts_m2(getItemValue(sensorName, 0))) # From http://wiki.wunderground.com/index.php/PWS_-_Upload_Protocol cmd = 'curl -s -G "' + WU_URL + '" ' \ + '--data-urlencode "action=updateraw" ' \ + '--data-urlencode "ID='+self.config.wunderground['stationdata']['station_id']+'" ' \ + '--data-urlencode "PASSWORD='******'stationdata']['station_key']+'" ' \ + '--data-urlencode "dateutc='+dateutc+'" ' \ + '--data-urlencode "softwaretype=openHAB" ' self.log.debug("") if self.config.wunderground['stationdata']['weather_upload']: self.log.debug("Below is the weather data that we will send:") else: self.log.debug( "Below is the weather data that we would send (if weather_upload was enabled):" ) if tempf is not None: cmd += '--data-urlencode "tempf=' + tempf + '" ' self.log.debug("tempf: " + tempf) if humidity is not None: cmd += '--data-urlencode "humidity=' + str(humidity) + '" ' self.log.debug("humidity: " + str(humidity)) if dewptf is not None: cmd += '--data-urlencode "dewptf=' + dewptf + '" ' self.log.debug("dewptf: " + dewptf) if heatidxf is not None: cmd += '--data-urlencode "heatidxf=' + heatidxf + '" ' self.log.debug("heatidxf: " + heatidxf) if soiltempf is not None: cmd += '--data-urlencode "soiltempf=' + soiltempf + '" ' self.log.debug("soiltempf: " + soiltempf) if soilmoisture is not None: cmd += '--data-urlencode "soilmoisture=' + soilmoisture + '" ' self.log.debug("soilmoisture: " + soilmoisture) if pressure is not None: cmd += '--data-urlencode "baromin=' + pressure + '" ' self.log.debug("baromin: " + pressure) if rainin is not None: cmd += '--data-urlencode "rainin=' + rainin + '" ' self.log.debug("rainin: " + rainin) if dailyrainin is not None: cmd += '--data-urlencode "dailyrainin=' + dailyrainin + '" ' self.log.debug("dailyrainin: " + dailyrainin) if winddir is not None: cmd += '--data-urlencode "winddir=' + winddir + '" ' self.log.debug("winddir: " + winddir) if windspeedmph is not None: cmd += '--data-urlencode "windspeedmph=' + windspeedmph + '" ' self.log.debug("windspeedmph: " + windspeedmph) if windgustmph is not None: cmd += '--data-urlencode "windgustmph=' + windgustmph + '" ' self.log.debug("windgustmph: " + windgustmph) if windgustdir is not None: cmd += '--data-urlencode "windgustdir=' + windgustdir + '" ' self.log.debug("windgustdir: " + windgustdir) if windspdmph_avg2m is not None: cmd += '--data-urlencode "windspdmph_avg2m=' + windspdmph_avg2m + '" ' self.log.debug("windspdmph_avg2m: " + windspdmph_avg2m) if winddir_avg2m is not None: cmd += '--data-urlencode "winddir_avg2m=' + winddir_avg2m + '" ' self.log.debug("winddir_avg2m: " + winddir_avg2m) if windgustmph_10m is not None: cmd += '--data-urlencode "windgustmph_10m=' + windgustmph_10m + '" ' self.log.debug("windgustmph_10m: " + windgustmph_10m) if windgustdir_10m is not None: cmd += '--data-urlencode "windgustdir_10m=' + windgustdir_10m + '" ' self.log.debug("windgustdir_10m: " + windgustdir_10m) if solarradiation is not None: cmd += '--data-urlencode "solarradiation=' + solarradiation + '" ' self.log.debug("solarradiation: " + solarradiation) cmd += ' 1>/dev/null 2>&1 &' self.log.debug("") if self.config.wunderground['stationdata']['weather_upload']: self.log.debug('WeatherUpload version ' + SCRIPT_VERSION + ', performing an upload. (loop count is: ' + str(wu_loop_count) + ')') self.log.debug('cmd: ' + cmd) os.system(cmd) else: self.log.debug('WeatherUpload version ' + SCRIPT_VERSION + ', skipping upload. (loop count is: ' + str(wu_loop_count) + ')') if (wu_loop_count % self.config.wunderground['stationdata']['upload_frequency'] == 0): wu_loop_count = 0 wu_loop_count = wu_loop_count + 1
def tts(ttsSay, ttsPrio=PRIO['MODERATE'], **keywords): ''' Text To Speak function. First argument is positional and mandatory. Remaining arguments are optionally keyword arguments. Example: tts("Hello") Example: tts("Hello", PRIO['HIGH'], ttsRoom='Kitchen', ttsVol=42, ttsLang='en-GB', ttsVoice='Brian') @param param1: Text to speak (positional argument) @param param2: Priority as defined by PRIO. Defaults to PRIO['MODERATE'] @param ttsRoom: Room to speak in @return: this is a description of what is returned ''' module_name = 'speak' log = logging.getLogger(LOG_PREFIX + '.' + module_name) log.setLevel(logging.INFO) def getDefaultRoom(): # Search for the default room to speak in for the_key, the_value in config.sonos['rooms'].iteritems(): if the_value['defaultttsdevice']: return the_key return 'All' if ((getItemValue('Sonos_Allow_TTS_And_Sounds', ON) != ON) and (ttsPrio <= PRIO['MODERATE'])): log.info( "Item Sonos_Allow_TTS_And_Sounds is OFF and ttsPrio is to low to speak \'" + ttsSay + "\' at this moment.") return False ttsRoom = getDefaultRoom( ) if 'ttsRoom' not in keywords else keywords['ttsRoom'] ttsRooms = [] if ttsRoom == 'All' or ttsRoom is None: for the_key, the_value in config.sonos['rooms'].iteritems(): ttsRooms.append(config.sonos['rooms'][the_key]) log.debug('TTS room found: ' + config.sonos['rooms'][the_key]['name']) else: sonosSpeaker = config.sonos['rooms'].get(ttsRoom, None) if sonosSpeaker is None: log.error("Room " + ttsRoom + " wasn't found in the sonos rooms dictionary") return ttsRooms.append(sonosSpeaker) log.debug('TTS room found: ' + sonosSpeaker['name']) for room in ttsRooms: ttsVol = None if 'ttsVol' not in keywords else keywords['ttsVol'] if not ttsVol or ttsVol >= 70: if ttsPrio == PRIO['LOW']: ttsVol = 30 elif ttsPrio == PRIO['MODERATE']: ttsVol = 40 elif ttsPrio == PRIO['HIGH']: ttsVol = 60 elif ttsPrio == PRIO['EMERGENCY']: ttsVol = 70 else: ttsVol = room['ttsvolume'] ttsLang = room['ttslang'] if 'ttsLang' not in keywords else keywords[ 'ttsLang'] ttsVoice = room[ 'ttsvoice'] if 'ttsVoice' not in keywords else keywords['ttsVoice'] ttsEngine = room[ 'ttsengine'] if 'ttsEngine' not in keywords else keywords[ 'ttsEngine'] #Voice.say(ttsSay, ttsEngine + ':' + ttsVoice, room['audiosink'], PercentType(10)) # Notification sound volume doesn't seem to be supported Voice.say(ttsSay, ttsEngine + ':' + ttsVoice, room['audiosink']) log.info("TTS: Speaking \'" + ttsSay + "\'" + ' in room: \'' + room['name'] + '\' at volume: \'' + str(ttsVol) + '\'.') return True