import math import logging import time from oauthlib.oauth2 import LegacyApplicationClient from requests_oauthlib import OAuth2Session __author__ = "Mark Ruys" __copyright__ = "Copyright 2020, Mark Ruys" __license__ = "MIT" __email__ = "mark@paracas.nl" # https://dev.netatmo.com/apidocumentation/weather class NetatmoApi: _BASE_URL = "https://api.netatmo.com/" def __init__(self, username, password, client_id, client_secret): self.username = username self.password = password self.client_id = client_id self.client_secret = client_secret def authorize(self): token_url = self._BASE_URL + "oauth2/token" client = LegacyApplicationClient(client_id=self.client_id) self.oauth = OAuth2Session(client=client, scope=['read_station']) self.oauth.fetch_token(token_url=token_url, username=self.username, password=self.password, client_id=self.client_id, client_secret=self.client_secret) def get_temperature(self, measures): for sensor in measures: for i, type in enumerate(measures[sensor]['type']): if type == 'temperature': for res in measures[sensor]['res']: return measures[sensor]['res'][res][i] return None def haversine_distance(self, lat1, lon1, lat2, lon2): R = 6371.0088 lat1 = math.radians(lat1) lon1 = math.radians(lon1) lat2 = math.radians(lat2) lon2 = math.radians(lon2) dlon = lon2 - lon1 dlat = lat2 - lat1 a = math.sin(dlat / 2) ** 2 + math.cos(lat1) * math.cos(lat2) * math.sin(dlon / 2) ** 2 c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a)) distance = R * c return distance * 1000 def get_location_temperature(self, latitude, longitude): delta = 0.002 for i in range(10): data = { "lat_ne" : latitude + delta, "lat_sw" : latitude - delta, "lon_ne" : longitude + delta, "lon_sw" : longitude - delta, "required_data": "temperature", "filter": True, } result = self.call('api/getpublicdata', data) if not (result and result['status'] == 'ok'): return None if len(result['body']) == 0: delta *= 2 logging.debug("Retry with delta {}".format(delta)) else: best_distance = 999999 for i, station in enumerate(result['body']): location = station['place']['location'] distance = self.haversine_distance(latitude, longitude, location[1], location[0]) if distance < best_distance: best_distance = distance best_station = station logging.info("Found device {} ({}, {}) at a distance of {:.0f} meters".format( best_station['_id'], best_station['place']['street'], best_station['place']['city'], best_distance, )) return self.get_temperature(best_station['measures']) return None def get_device_temperature(self, device_id): data = { "device_id" : device_id } result = self.call('api/getpublicmeasure', data) if result and result['status'] == 'ok': for station in result['body']: temperature = self.get_temperature(station['measures']) if temperature is not None: return temperature return None def call(self, command, payload): for i in range(1, 4): response = self.oauth.get(self._BASE_URL + command, data=payload) logging.debug(response.json()) if response.status_code == 200: return response.json() time.sleep(i ** 3) return None