def send_message(self, text, chat_id=None): if self.__running: chat_ids = self.__chat_ids if chat_id is None else [int(chat_id)] for chat_id in chat_ids: url = self.__bot_url + 'sendMessage?text={}&chat_id={}'.format( text, chat_id) terrariumUtils.get_remote_data(url, proxy=self.__proxy)
def load_data(self): logger.info('Update {} data from ONLINE refreshing cache.'.format( self.get_type())) self.credits['text'] = 'OpenWeatherMap weather data' parsed_json = terrariumUtils.get_remote_data(self.source) if parsed_json is not None: # Parse general data information self.location['city'] = parsed_json['name'] self.location['country'] = parsed_json['sys']['country'] self.location['geo']['lat'] = float(parsed_json['coord']['lat']) self.location['geo']['long'] = float(parsed_json['coord']['lon']) self.credits['url'] = 'https://openweathermap.org/city/{}'.format( parsed_json['id']) self.sun['rise'] = int(parsed_json['sys']['sunrise']) self.sun['set'] = int(parsed_json['sys']['sunset']) parsed_json = terrariumUtils.get_remote_data( self.source.replace('/weather?q', '/forecast?q')) if parsed_json is not None: # Parse hourly and week forecast datelimit = int(time.time()) + ( 2 * 24 * 60 * 60) # Hourly forecast limit of 2 days for forecast in parsed_json['list']: forecast_data = { 'from': int(forecast['dt']), 'to': int(forecast['dt']) + (3 * 60 * 60), # Data is provided per 3 hours 'weather': forecast['weather'][0]['description'], 'rain': (float(forecast['rain']['3h']) / 3.0) if 'rain' in forecast and '3h' in forecast['rain'] else 0, # Guess in mm 'humidity': float(forecast['main']['humidity']), 'wind_direction': forecast['wind']['deg'], 'wind_speed': float(forecast['wind']['speed']), 'temperature': float(forecast['main']['temp']), 'pressure': float(forecast['main']['pressure']) } self.week_forecast[forecast_data['from']] = forecast_data if forecast_data['to'] <= datelimit: self.hour_forecast[ forecast_data['from']] = forecast_data else: logger.error('Error getting online data from {}'.format( self.get_type())) return False return True
def set_hardware_state(self, state, force = False): changed = True if self.__firmware is None: logger.error('Sonoff device is not connected. Cannot trigger power switch') changed = False else: data = re.match(self.VALID_SOURCE,self.get_address()) if data: data = data.groupdict() url = None if 'tasmota' == self.__firmware: url = 'http://{}/cm?cmnd=Power%20{}'.format(data['host'],('1' if state else '0')) if 'user' in data and 'password' in data: url += '&user={}&password={}'.format(data['user'],data['password']) elif 'espeasy' == self.__firmware: url = 'http://{}/control?cmd=event,T{}'.format(data['host'],('1' if state else '0')) elif 'espurna' == self.__firmware: url = 'http://{}/api/relay/0?apikey={}&value={}'.format(data['host'],data['password'],('1' if state else '0')) state = terrariumUtils.get_remote_data(url) if state is None: changed = False return changed
def _get_hardware_value(self): data = terrariumUtils.get_remote_data(self.device) if data is None: return None data = float(data) return data
def load_data(self): self.sunrise = int(datetime.now().replace(hour=8, minute=0, second=0).strftime('%s')) self.sunset = self.sunrise + (12 * 60 * 60) logger.info('Update Wunderground data from ONLINE refreshing cache.') self.type = 'weather.com' self.copyright = {'text': 'Wunderground weather data', 'url': ''} parsed_json = terrariumUtils.get_remote_data(self.source_url) if parsed_json is not None: # Parse general data information self.city = parsed_json['location']['city'] self.country = parsed_json['location']['country_name'] self.geo['lat'] = float(parsed_json['location']['lat']) self.geo['long'] = float(parsed_json['location']['lon']) self.copyright['url'] = parsed_json['location']['wuiurl'] now = datetime.now() self.sunrise = time.mktime( now.replace( hour=int(parsed_json['sun_phase']['sunrise']['hour']), minute=int(parsed_json['sun_phase']['sunrise']['minute']), second=0).timetuple()) now = datetime.now( ) # Not sure if needed. But this will never fail! self.sunset = time.mktime( now.replace( hour=int(parsed_json['sun_phase']['sunset']['hour']), minute=int(parsed_json['sun_phase']['sunset']['minute']), second=0).timetuple()) # Parse hourly and week forecast self.hour_forecast = [] self.week_forecast = [] datelimit = int(time.time()) + ( 2 * 24 * 60 * 60) # Hourly forecast limit of 2 days for forecast in parsed_json['hourly_forecast']: forecast_hour = { 'from': int(forecast['FCTTIME']['epoch']), 'to': int(forecast['FCTTIME']['epoch']) + (60 * 60), # Data is provided per 1 hour 'weather': forecast['condition'], 'rain': 0, # Figure out the data 'humidity': float(forecast['humidity']), 'wind_direction': forecast['wdir']['dir'], 'wind_speed': float(forecast['wspd']['metric']) / 3.6, 'temperature': float(forecast['temp']['metric']), 'pressure': float(forecast['mslp']['metric']) } self.week_forecast.append(copy.deepcopy(forecast_hour)) if forecast_hour['to'] <= datelimit: self.hour_forecast.append(copy.deepcopy(forecast_hour)) else: logger.error('Error getting online data from weather.com') return False return True
def __checker(self): logger.info('Start terrariumPI door checker for door \'%s\'' % self.get_name()) while True: current_status = None if self.get_hardware_type() == 'gpio': current_status = terrariumDoor.OPEN if GPIO.input(terrariumUtils.to_BCM_port_number(self.get_address())) else terrariumDoor.CLOSED elif self.get_hardware_type() == 'remote' and (int(time.time()) - self.__last_check) >= terrariumDoor.REMOTE_TIMEOUT: current_status = None url_data = terrariumUtils.parse_url(self.get_address()) if url_data is False: logger.error('Remote url \'%s\' for door \'%s\' is not a valid remote source url!' % (self.get_address(),self.get_name())) else: data = terrariumUtils.get_remote_data(self.get_address()) if data is not None: current_status = terrariumDoor.OPEN if terrariumUtils.is_true(data) else terrariumDoor.CLOSED else: logger.warning('Remote door \'%s\' got error from remote source \'%s\'' % (self.get_name(),self.get_address())) self.__last_check = int(time.time()) logger.debug('Current door \'%s\' status: %s' % (self.get_name(),current_status)) if current_status != self.get_status(): logger.info('Door \'%s\' changed from %s to %s' % (self.get_name(),self.get_status(), current_status)) self.set_status(current_status) if self.callback is not None: self.callback(self.get_data()) sleep(terrariumDoor.CHECKER_TIMEOUT)
def _load_hardware(self): # Input format should be either: # - http://[HOST]#[POWER_SWITCH_NR] # - http://[HOST]/#[POWER_SWITCH_NR] # - http://[PASSWORD]@[HOST]#[POWER_SWITCH_NR] # - http://[PASSWORD]@[HOST]/#[POWER_SWITCH_NR] address = self._address # Try Tasmota # http://sonoff/cm?cmnd=Power[POWER_SWITCH_NR]%201 # http://sonoff/cm?cmnd=Power[POWER_SWITCH_NR]%200 # http://sonoff/cm?user=admin&password=joker&cmnd=Power[POWER_SWITCH_NR]%201 device = f'{address["protocol"]}://{address["host"]}/cm?' if 'user' in address and 'password' in address: device += f'user={address["user"]}&password={address["password"]}&' device += 'cmnd=' state = terrariumUtils.get_remote_data(f'{device}Status%200') if state is None: return None # Always overule the ID generating, as we want to use the MAC as that is unique if the IP address is changing self.id = md5(f'{self.HARDWARE}{state["StatusNET"]["Mac"].lower()}'. encode()).hexdigest() return device
def load_data(self): logger.info('Update {} data from ONLINE refreshing cache.'.format( self.get_type())) self.credits['text'] = 'Wunderground weather data' parsed_json = terrariumUtils.get_remote_data(self.source) if parsed_json is not None: # Parse general data information self.location['city'] = parsed_json['location']['city'] self.location['country'] = parsed_json['location']['country_name'] self.location['geo']['lat'] = float(parsed_json['location']['lat']) self.location['geo']['long'] = float( parsed_json['location']['lon']) self.credits['url'] = parsed_json['location']['wuiurl'] now = datetime.now() self.sun['rise'] = int( time.mktime( now.replace( hour=int(parsed_json['sun_phase']['sunrise']['hour']), minute=int( parsed_json['sun_phase']['sunrise']['minute']), second=0).timetuple())) now = datetime.now() self.sun['set'] = int( time.mktime( now.replace( hour=int(parsed_json['sun_phase']['sunset']['hour']), minute=int( parsed_json['sun_phase']['sunset']['minute']), second=0).timetuple())) # Parse hourly and week forecast datelimit = int(time.time()) + ( 2 * 24 * 60 * 60) # Hourly forecast limit of 2 days for forecast in parsed_json['hourly_forecast']: forecast_data = { 'from': int(forecast['FCTTIME']['epoch']), 'to': int(forecast['FCTTIME']['epoch']) + (60 * 60), # Data is provided per 1 hour 'weather': forecast['condition'], 'rain': 0.0, # Figure out the data 'humidity': float(forecast['humidity']), 'wind_direction': forecast['wdir']['dir'], 'wind_speed': float(forecast['wspd']['metric']) / 3.6, 'temperature': float(forecast['temp']['metric']), 'pressure': float(forecast['mslp']['metric']) } self.week_forecast[forecast_data['from']] = forecast_data if forecast_data['to'] <= datelimit: self.hour_forecast[forecast_data['from']] = forecast_data else: logger.error('Error getting online data from {}'.format( self.get_type())) return False return True
def set_hardware_state(self, state, force = False): changed = True if self.__firmware is None: if self.__retries < 5: self.__retries += 1 logger.warning('Sonoff device is not connected for trigger action. Reconnect attempt: {}'.format(self.__retries)) self.load_hardware() return self.set_hardware_state(state,force) else: logger.error('Sonoff device is not connected. Cannot trigger power switch') return False data = re.match(self.VALID_SOURCE,self.get_address()) if data: data = data.groupdict() url = None if 'tasmota' == self.__firmware: url = self.url + '%20{}'.format('1' if state else '0') elif 'espeasy' == self.__firmware: url = 'http://{}/control?cmd=event,T{}'.format(data['host'],('1' if state else '0')) elif 'espurna' == self.__firmware: url = 'http://{}/api/relay/0?apikey={}&value={}'.format(data['host'],data['password'],('1' if state else '0')) state = terrariumUtils.get_remote_data(url) if state is None: changed = False return changed
def _get_hardware_value(self): url = f'{self.device}Dimmer' data = terrariumUtils.get_remote_data(url) if data is not None and 'Dimmer' in data: return int(data['Dimmer']) return None
def get_hardware_state(self): data = None url_data = terrariumUtils.parse_url(self.get_address()) if url_data is False: logger.error('Remote url \'%s\' for switch \'%s\' is not a valid remote source url!' % (self.get_address(),self.get_name())) else: data = terrariumUtils.get_remote_data(self.get_address()) return terrariumPowerSwitch.ON if terrariumUtils.is_true(data) else terrariumPowerSwitch.OFF
def _set_hardware_value(self, state): state = int(max(0.0, min(100.0, float(state + self._dimmer_offset)))) url = f'{self.device}Dimmer%20{state}' data = terrariumUtils.get_remote_data(url) if data is None: return False return state == int(data['Dimmer'])
def __get_updates(self,offset=None): self.__last_update_check = int(time.time()) url = self.__bot_url + 'getUpdates?timeout={}'.format(terrariumNotificationTelegramBot.__POLL_TIMEOUT) if offset: url += '&offset={}'.format(offset) data = terrariumUtils.get_remote_data(url,terrariumNotificationTelegramBot.__POLL_TIMEOUT + 3,proxy=self.__proxy) if data is None: data = {'description' : 'Did not receive valid JSON data'} return data
def get_raw_data(self): logger.debug('Using URL: %s' % (self.location,)) try: remote_image = terrariumUtils.get_remote_data(self.location) if remote_image is None: raise terrariumWebcamRAWUpdateException() self.raw_image = BytesIO(remote_image) return True except terrariumWebcamRAWUpdateException as ex: logger.warning('Error getting raw online image from webcam \'%s\' with error message: %s' % (self.get_name(),ex)) return False
def _set_hardware_value(self, state): action = 1 if state == self.ON else 0 url = f'{self.device}Power{self._address["nr"]}%20{action}' data = terrariumUtils.get_remote_data(url) if data is None: return False if 'POWER' in data: data = data['POWER'] elif f'POWER{self._address["nr"]}' in data: data = data[f'POWER{self._address["nr"]}'] return state == (self.ON if terrariumUtils.is_true(data) else self.OFF)
def get_hardware_state(self): data = None if self.__firmware is None: logger.error( 'Sonoff device is not connected. Cannot read power switch state' ) return terrariumPowerSwitch.OFF else: data = re.match(self.VALID_SOURCE, self.get_address()) if data: data = data.groupdict() url = None if 'tasmota' == self.__firmware: url = 'http://{}/cm?cmnd=Power'.format(data['host']) if 'user' in data and 'password' in data: url += '&user={}&password={}'.format( data['user'], data['password']) elif 'espeasy' == self.__firmware: url = 'http://{}/json'.format(data['host']) elif 'espurna' == self.__firmware: if 'password' not in data: # Just add dummy value... data['password'] = '******' url = 'http://{}/apis?apikey={}'.format( data['host'], data['password']) state = terrariumUtils.get_remote_data(url) if state is None: logger.warning( 'Error reading Sonoff \'{}\' power state. So retruning last known state: {}' .format(self.get_name(), self.state)) return self.state if 'tasmota' == self.__firmware: return terrariumPowerSwitch.ON if terrariumUtils.is_true( state['POWER']) else terrariumPowerSwitch.OFF elif 'espeasy' == self.__firmware: return terrariumPowerSwitch.ON if terrariumUtils.is_true( state['POWER']) else terrariumPowerSwitch.OFF elif 'espurna' == self.__firmware: return terrariumPowerSwitch.ON if terrariumUtils.is_true( state['POWER']) else terrariumPowerSwitch.OFF return terrariumPowerSwitch.OFF
def __load_history_data(self): # Onecall API's are more expensive (max 1000 a day - 1 call per 2 minutes) so we update this at a lower frequency # Here we can do 1 hit a day. As the history is per hole full day at a time, and will not change anymore if self.__history_day is not None and self.__history_day == int( datetime.utcfromtimestamp( int(datetime.now().timestamp()) + self._data["timezone"]).strftime('%d')): return True start = time() self._data['history'] = [] address = terrariumUtils.parse_url(self.address) for day in range(1, 3): now = int(datetime.now().timestamp()) + self._data["timezone"] - ( day * 24 * 60 * 60) history_url = 'https://api.openweathermap.org/data/2.5/onecall/timemachine?lat={}&lon={}&units=metric&dt={}&appid={}&lang={}'.format( self._data['geo']['lat'], self._data['geo']['long'], now, address['query_params']['appid'], self._device['language'][0:2]) data = terrariumUtils.get_remote_data(history_url) if data is None: continue for item in data['hourly']: self._data['history'].append({ 'timestamp': int(item["dt"] + self._data["timezone"]), 'temperature': float(item['temp']), 'humidity': float(item['humidity']), 'pressure': float(item['pressure']), 'uvi': float(item['uvi']), }) self._data['history'] = sorted(self._data['history'], key=lambda d: d['timestamp']) self.__history_day = int( datetime.utcfromtimestamp( int(datetime.now().timestamp()) + self._data["timezone"]).strftime('%d')) logger.info( f'Loaded new historical weather data ({len(self._data["history"])} measurements) from {datetime.fromtimestamp(int(self._data["history"][0]["timestamp"]))} till {datetime.fromtimestamp(int(self._data["history"][len(self._data["history"])-1]["timestamp"]))} in {time()-start:.2f} seconds.' ) return True
def get_hardware_state(self): data = None if self.__firmware is None: if self.__retries < 5: self.__retries += 1 logger.warning('Sonoff device is not connected while reading the state. Reconnect attempt: {}'.format(self.__retries)) self.load_hardware() return self.get_hardware_state() else: logger.error('Sonoff device is not connected. Cannot read power switch state') return terrariumPowerSwitch.OFF data = re.match(self.VALID_SOURCE,self.get_address()) if data: data = data.groupdict() url = None if 'tasmota' == self.__firmware: url = self.url elif 'espeasy' == self.__firmware: url = 'http://{}/json'.format(data['host']) elif 'espurna' == self.__firmware: if 'password' not in data: # Just add dummy value... data['password'] = '******' url = 'http://{}/apis?apikey={}'.format(data['host'],data['password']) state = terrariumUtils.get_remote_data(url) if state is None: logger.warning('Error reading Sonoff \'{}\' power state. So returning last known state: {}'.format(self.get_name(),self.state)) return self.state if 'tasmota' == self.__firmware: for state_name, state_value in state.items(): if state_name.startswith('POWER'): return terrariumPowerSwitch.ON if terrariumUtils.is_true(state_value) else terrariumPowerSwitch.OFF elif 'espeasy' == self.__firmware: return terrariumPowerSwitch.ON if terrariumUtils.is_true(state['POWER']) else terrariumPowerSwitch.OFF elif 'espurna' == self.__firmware: return terrariumPowerSwitch.ON if terrariumUtils.is_true(state['POWER']) else terrariumPowerSwitch.OFF return terrariumPowerSwitch.OFF
def _get_hardware_value(self): data = self.__cache.get_data(self.__cache_key) if data is None: # Cache is expired, so we update with new data # Get the overall state information url = f'{self.device}State' data = terrariumUtils.get_remote_data(url) if data is None: return None self.__cache.set_data(self.__cache_key, data, self._CACHE_TIMEOUT) if 'POWER' in data: data = data['POWER'] elif f'POWER{self._address["nr"]}' in data: data = data[f'POWER{self._address["nr"]}'] return self.ON if terrariumUtils.is_true(data) else self.OFF
def _load_hardware(self): # Input format should be either: # - http://[HOST]#[POWER_SWITCH_NR] # - http://[HOST]/#[POWER_SWITCH_NR] # - http://[PASSWORD]@[HOST]#[POWER_SWITCH_NR] # - http://[PASSWORD]@[HOST]/#[POWER_SWITCH_NR] address = self._address # Try Tasmota # http://sonoff/cm?cmnd=Power[POWER_SWITCH_NR]%201 # http://sonoff/cm?cmnd=Power[POWER_SWITCH_NR]%200 # http://sonoff/cm?user=admin&password=joker&cmnd=Power[POWER_SWITCH_NR]%201 device = f'{address["protocol"]}://{address["host"]}/cm?' if 'user' in address and 'password' in address: device += f'user={address["user"]}&password={address["password"]}&' device += 'cmnd=' state = terrariumUtils.get_remote_data(f'{device}Status%200') if state is None: return None # Create the cache key for caching the relay states. # This is usefull when there are more then 1 relay per hardware device. self.__cache_key = md5( f'{self.HARDWARE}{state["StatusNET"]["Mac"].lower()}'.encode( )).hexdigest() self.__cache = terrariumCache() self.__cache.set_data(self.__cache_key, state['StatusSTS'], self._CACHE_TIMEOUT) # We need the use the address_nr value also, as there can multiple relays per sonoff device. if self._device['id'] is None: self.id = md5( f'{self.HARDWARE}{state["StatusNET"]["Mac"].lower()}{address["nr"]}' .encode()).hexdigest() return device
def _get_data(self): value = self._sensor_cache.get_data(self.__source_cache_key) if value is None: value = terrariumUtils.get_remote_data(self.device) if value is None: return None self._sensor_cache.set_data(self.__source_cache_key,value, self._CACHE_TIMEOUT) for item in self.__json_path: # Dirty hack to process array data.... try: item = int(item) except Exception as ex: item = str(item) value = value[item] data = { self.sensor_type : value } return data
def __load_general_data(self): address = self.address + '&units=metric&lang=' + self._device[ 'language'][0:2] logger.debug('Loading weather source {}'.format(address)) data = terrariumUtils.get_remote_data(self.address) if data: self._data['city'] = data['name'] self._data['country'] = data['sys']['country'] self._data['geo'] = { 'lat': float(data['coord']['lat']), 'long': float(data['coord']['lon']) } self._data['url'] = 'https://openweathermap.org/city/{}'.format( data['id']) self._data['credits'] = 'OpenWeatherMap weather data' self._data['timezone'] = int(data['timezone']) return True logger.warning( 'Error loading online weather data from source {} !'.format( address)) return False
def load_hardware(self): self.__firmware = None # Input format should be either: # - http://[HOST]#[POWER_SWITCH_NR] # - http://[HOST]/#[POWER_SWITCH_NR] # - http://[PASSWORD]@[HOST]#[POWER_SWITCH_NR] # - http://[PASSWORD]@[HOST]/#[POWER_SWITCH_NR] data = re.match(self.VALID_SOURCE,self.get_address()) if data: data = data.groupdict() try: # Try Tasmota # http://sonoff/cm?cmnd=Power%20TOGGLE # http://sonoff/cm?cmnd=Power%20On # http://sonoff/cm?cmnd=Power%20off # http://sonoff/cm?user=admin&password=joker&cmnd=Power%20Toggle url = 'http://{}/cm?cmnd=Power'.format(data['host']) if 'user' in data and 'password' in data: url += '&user={}&password={}'.format(data['user'],data['password']) state = terrariumUtils.get_remote_data(url) if state is None: raise Exception('No data, jump to next test') self.__firmware = 'tasmota' except Exception as ex: print('Tasmota exceptions') print(ex) if self.__firmware is None: try: # Try ESP Easy # url_switch_on = 'http://192.168.1.42/control?cmd=event,T1' # url_switch_off = 'http://192.168.1.42/control?cmd=event,T0' #print('Test ESP Easy') url = 'http://{}/json'.format(data['host']) # No information about using username and password: # https://www.letscontrolit.com/wiki/index.php?title=ESPEasy_Command_Reference # https://www.letscontrolit.com/wiki/index.php?title=ESP_Easy_web_interface#JSON_page_.28hidden_prior_to_version_2.0.2B.29 #print(url) state = terrariumUtils.get_remote_data(url) #print('Result') #print(state) if state is None: raise Exception('No data, jump to next test') self.__firmware = 'espeasy' except Exception as ex: print('ESP Easy exceptions') print(ex) if self.__firmware is None: try: # Try ESPurna # https://github.com/xoseperez/espurna/wiki/RESTAPI # http://192.168.1.108/apis?apikey=C62ED7BE7593B658 # http://192.168.1.108/api/relay/0?apikey=C62ED7BE7593B658&value=0 (off) # http://192.168.1.108/api/relay/0?apikey=C62ED7BE7593B658&value=1 (on) # http://192.168.1.108/api/relay/0?apikey=C62ED7BE7593B658&value=2 (toggle) #print('Test ESPurna') if 'password' not in data: # Just add dummy value... data['password'] = '******' url = 'http://{}/apis?apikey={}'.format(data['host'],data['password']) #print(url) state = terrariumUtils.get_remote_data(url,json=True) #print('Result') #print(state) if state is None: raise Exception('No data, this was the last attempt...') self.__firmware = 'espurna' except Exception as ex: print('ESPurna exceptions') print(ex)
def load_data(self): data = terrariumUtils.get_remote_data(self.get_address()) if data is None: return None return {self.get_sensor_type(): data}
def __get_raw_data(self): if self.__url is not None: self.__value = terrariumUtils.get_remote_data(self.__url)
def load_data(self): self.sunrise = int(datetime.now().replace(hour=8, minute=0, second=0).strftime('%s')) self.sunset = self.sunrise + (12 * 60 * 60) starttime = time.time() logger.info('Update YR.no data from ONLINE refreshing cache.') self.type = 'yr.no' xmldata = terrariumUtils.get_remote_data( self.source_url.strip('/') + '/forecast_hour_by_hour.xml') if xmldata is not None: try: xmldata = untangle.parse(xmldata) except Exception: logger.exception('Error getting online data from yr.no') return False # Parse hour forecast # Parse general data information self.city = xmldata.weatherdata.location.name.cdata self.country = xmldata.weatherdata.location.country.cdata self.geo['lat'] = float( xmldata.weatherdata.location.location['latitude']) self.geo['long'] = float( xmldata.weatherdata.location.location['longitude']) self.copyright['text'] = xmldata.weatherdata.credit.link['text'] self.copyright['url'] = xmldata.weatherdata.credit.link['url'] self.sunrise = time.mktime( dateutil.parser.parse( xmldata.weatherdata.sun['rise']).timetuple()) self.sunset = time.mktime( dateutil.parser.parse( xmldata.weatherdata.sun['set']).timetuple()) self.hour_forecast = [] for forecast in xmldata.weatherdata.forecast.tabular.time: self.hour_forecast.append({ 'from': time.mktime( dateutil.parser.parse(forecast['from']).timetuple()), 'to': time.mktime( dateutil.parser.parse(forecast['to']).timetuple()), 'weather': forecast.symbol['name'], 'rain': float(forecast.precipitation['value']), 'humidity': 0, 'wind_direction': forecast.windDirection['name'], 'wind_speed': float(forecast.windSpeed['mps']), 'temperature': float(forecast.temperature['value']), 'pressure': float(forecast.pressure['value']) }) # Parse week forecast self.week_forecast = [] xmldata = terrariumUtils.get_remote_data( self.source_url.strip('/') + '/forecast.xml') if xmldata is not None: try: xmldata = untangle.parse(xmldata) except Exception: logger.exception('Error getting online data from yr.no') #xmldata = untangle.parse(self.source_url.strip('/') + '/forecast.xml') for forecast in xmldata.weatherdata.forecast.tabular.time: self.week_forecast.append({ 'from': time.mktime( dateutil.parser.parse( forecast['from']).timetuple()), 'to': time.mktime( dateutil.parser.parse(forecast['to']).timetuple()), 'weather': forecast.symbol['name'], 'rain': float(forecast.precipitation['value']), 'humidity': 0, 'wind_direction': forecast.windDirection['name'], 'wind_speed': float(forecast.windSpeed['mps']), 'temperature': float(forecast.temperature['value']), 'pressure': float(forecast.pressure['value']) }) else: logger.error('Error getting online data from yr.no') else: logger.error('Error getting online data from yr.no') return False return True
def load_data(self): self.sunrise = int(datetime.now().replace(hour=8, minute=0, second=0).strftime('%s')) self.sunset = self.sunrise + (12 * 60 * 60) logger.info('Update OpenWeatherMap data from ONLINE refreshing cache.') self.type = 'openweathermap.org' self.copyright = { 'text': 'OpenWeatherMap data', 'url': 'https://openweathermap.org/city/' } parsed_json = terrariumUtils.get_remote_data(self.source_url) if parsed_json is not None: # Parse general data information self.city = parsed_json['name'] self.country = parsed_json['sys']['country'] self.geo['lat'] = float(parsed_json['coord']['lat']) self.geo['long'] = float(parsed_json['coord']['lon']) self.copyright['url'] = 'https://openweathermap.org/city/' + str( parsed_json['id']) self.sunrise = parsed_json['sys']['sunrise'] self.sunset = parsed_json['sys']['sunset'] self.hour_forecast = [] self.week_forecast = [] parsed_json = terrariumUtils.get_remote_data( self.source_url.replace('/weather?q', '/forecast?q')) if parsed_json is not None: # Parse hourly and week forecast datelimit = int(time.time()) + ( 2 * 24 * 60 * 60) # Hourly forecast limit of 2 days for forecast in parsed_json['list']: forecast_hour = { 'from': forecast['dt'], 'to': forecast['dt'] + (3 * 60 * 60), # Data is provided per 3 hours 'weather': forecast['weather'][0]['description'], 'rain': (float(forecast['rain']['3h']) / 3.0) if 'rain' in forecast and '3h' in forecast['rain'] else 0, # Guess in mm 'humidity': float(forecast['main']['humidity']), 'wind_direction': forecast['wind']['deg'], 'wind_speed': float(forecast['wind']['speed']) / 3.6, 'temperature': float(forecast['main']['temp']), 'pressure': float(forecast['main']['pressure']) } self.week_forecast.append(copy.deepcopy(forecast_hour)) if forecast_hour['to'] <= datelimit: self.hour_forecast.append(copy.deepcopy(forecast_hour)) else: logger.error('Error getting online data from openweathermap.org') return False return True
def _load_hardware(self): data = terrariumUtils.get_remote_data(self.address) if data is not None: return self.address return None
def load_data(self): starttime = time.time() logger.info('Update {} data from ONLINE refreshing cache.'.format( self.get_type())) xmldata = terrariumUtils.get_remote_data( self.source.strip('/') + '/forecast_hour_by_hour.xml') if xmldata is not None: try: xmldata = untangle.parse(xmldata) except Exception: logger.exception('Error getting online data from {}'.format( self.get_type())) return False # Parse hour forecast # Parse general data information self.location['city'] = xmldata.weatherdata.location.name.cdata self.location[ 'country'] = xmldata.weatherdata.location.country.cdata self.location['geo']['lat'] = float( xmldata.weatherdata.location.location['latitude']) self.location['geo']['long'] = float( xmldata.weatherdata.location.location['longitude']) self.credits['text'] = xmldata.weatherdata.credit.link['text'] self.credits['url'] = xmldata.weatherdata.credit.link['url'] self.sun['rise'] = int( time.mktime( dateutil.parser.parse( xmldata.weatherdata.sun['rise']).timetuple())) self.sun['set'] = int( time.mktime( dateutil.parser.parse( xmldata.weatherdata.sun['set']).timetuple())) for forecast in xmldata.weatherdata.forecast.tabular.time: forecast_data = { 'from': int( time.mktime( dateutil.parser.parse( forecast['from']).timetuple())), 'to': int( time.mktime( dateutil.parser.parse( forecast['to']).timetuple())), 'weather': forecast.symbol['name'], 'rain': float(forecast.precipitation['value']), 'humidity': 0.0, 'wind_direction': forecast.windDirection['name'], 'wind_speed': float(forecast.windSpeed['mps']), 'temperature': float(forecast.temperature['value']), 'pressure': float(forecast.pressure['value']) } self.hour_forecast[forecast_data['from']] = forecast_data # Parse week forecast xmldata = terrariumUtils.get_remote_data( self.source.strip('/') + '/forecast.xml') if xmldata is not None: try: xmldata = untangle.parse(xmldata) except Exception: logger.exception( 'Error getting online data from {}'.format( self.get_type())) for forecast in xmldata.weatherdata.forecast.tabular.time: forecast_data = { 'from': int( time.mktime( dateutil.parser.parse( forecast['from']).timetuple())), 'to': int( time.mktime( dateutil.parser.parse( forecast['to']).timetuple())), 'weather': forecast.symbol['name'], 'rain': float(forecast.precipitation['value']), 'humidity': 0.0, 'wind_direction': forecast.windDirection['name'], 'wind_speed': float(forecast.windSpeed['mps']), 'temperature': float(forecast.temperature['value']), 'pressure': float(forecast.pressure['value']) } self.week_forecast[forecast_data['from']] = forecast_data else: logger.error('Error getting online data from {}'.format( self.get_type())) else: logger.error('Error getting online data from {}'.format( self.get_type())) return False return True
def update(self, force=False): now = datetime.datetime.now() if now - self.last_update > datetime.timedelta( seconds=terrariumSensor.UPDATE_TIMEOUT) or force: logger.debug( 'Updating %s %s sensor \'%s\'' % (self.get_hardware_type(), self.get_type(), self.get_name())) old_current = self.get_current() current = None try: starttime = time.time() if 'remote' == self.get_hardware_type(): url_data = terrariumUtils.parse_url(self.get_address()) if url_data is False: logger.error( 'Remote url \'%s\' for sensor \'%s\' is not a valid remote source url!' % (self.get_address(), self.get_name())) else: data = terrariumUtils.get_remote_data( self.get_address()) if data is not None: current = float(data) else: logger.warning( 'Remote sensor \'%s\' got error from remote source \'%s\': %s' % (self.get_name(), self.get_address(), data.status_code)) elif 'hc-sr04' == self.get_hardware_type(): GPIO.output( terrariumUtils.to_BCM_port_number( self.sensor_address['TRIG']), False) time.sleep(2) GPIO.output( terrariumUtils.to_BCM_port_number( self.sensor_address['TRIG']), True) time.sleep(0.00001) GPIO.output( terrariumUtils.to_BCM_port_number( self.sensor_address['TRIG']), False) pulse_start = time.time() while GPIO.input( terrariumUtils.to_BCM_port_number( self.sensor_address['ECHO'])) == 0: pulse_start = time.time() pulse_end = time.time() while GPIO.input( terrariumUtils.to_BCM_port_number( self.sensor_address['ECHO'])) == 1: pulse_end = time.time() pulse_duration = pulse_end - pulse_start # https://www.modmypi.com/blog/hc-sr04-ultrasonic-range-sensor-on-the-raspberry-pi # Measure in centimetre current = round(pulse_duration * 17150, 2) elif 'sku-sen0161' == self.get_hardware_type(): # Do multiple measurements... values = [] for counter in range(5): analog_port = MCP3008(channel=int(self.get_address())) # https://github.com/theyosh/TerrariumPI/issues/108 # We measure the values in volts already, so no deviding by 1000 as original script does values.append((analog_port.value * (5000.0 / 1024.0)) * 3.3 + 0.1614) time.sleep(0.2) # sort values from low to high values.sort() # Calculate average. Exclude the min and max value. And therefore devide by 3 current = round((sum(values[1:-1]) / 3.0), 2) elif 'temperature' == self.get_type(): if self.get_hardware_type() == 'owfs': current = float(self.sensor.temperature) elif self.get_hardware_type() == 'w1': data = '' with open( terrariumSensor.W1_BASE_PATH + self.get_address() + '/w1_slave', 'r') as w1data: data = w1data.read() w1data = terrariumSensor.W1_TEMP_REGEX.search(data) if w1data: # Found data current = float(w1data.group('value')) / 1000 elif self.get_hardware_type( ) in terrariumSensor.VALID_DHT_SENSORS.keys(): time.sleep(2.1) humidity, temperature = self.sensor.read_retry( terrariumSensor.VALID_DHT_SENSORS[ self.get_hardware_type()], float( terrariumUtils.to_BCM_port_number( self.sensor_address)), 5) if temperature is not None: current = float(temperature) elif 'humidity' == self.get_type(): if self.get_hardware_type() == 'owfs': current = float(self.sensor.humidity) elif self.get_hardware_type() == 'w1': # Not tested / No hardware to test with pass elif self.get_hardware_type( ) in terrariumSensor.VALID_DHT_SENSORS.keys(): time.sleep(2.1) humidity, temperature = self.sensor.read_retry( terrariumSensor.VALID_DHT_SENSORS[ self.get_hardware_type()], float( terrariumUtils.to_BCM_port_number( self.sensor_address)), 5) if humidity is not None: current = float(humidity) if current is None or not (self.get_limit_min() <= current <= self.get_limit_max()): # Invalid current value.... log and ingore logger.warn( 'Measured value %s%s from %s sensor \'%s\' is outside valid range %.2f%s - %.2f%s in %.5f seconds.' % (current, self.get_indicator(), self.get_type(), self.get_name(), self.get_limit_min(), self.get_indicator(), self.get_limit_max(), self.get_indicator(), time.time() - starttime)) else: self.current = current self.last_update = now logger.info( 'Updated %s sensor \'%s\' from %.2f%s to %.2f%s in %.5f seconds' % (self.get_type(), self.get_name(), old_current, self.get_indicator(), self.get_current(), self.get_indicator(), time.time() - starttime)) except Exception, ex: print ex logger.exception( 'Error updating %s %s sensor \'%s\' with error:' % (self.get_hardware_type(), self.get_type(), self.get_name()))