""" Support for Google Geocode sensors. For more details about this platform, please refer to the documentation at https://github.com/michaelmcarthur/GoogleGeocode-HASS Written By Michael McArthur https://github.com/michaelmcarthur/GoogleGeocode-HASS """ from datetime import datetime from datetime import timedelta import logging import json import requests from requests import get import voluptuous as vol from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import ( CONF_API_KEY, CONF_NAME, CONF_SCAN_INTERVAL, ATTR_ATTRIBUTION, ATTR_LATITUDE, ATTR_LONGITUDE) import homeassistant.helpers.location as location from homeassistant.util import Throttle from homeassistant.helpers.entity import Entity import homeassistant.helpers.config_validation as cv _LOGGER = logging.getLogger(__name__) CONF_ORIGIN = 'origin' CONF_OPTIONS = 'options' CONF_DISPLAY_ZONE = 'display_zone' CONF_ATTRIBUTION = "Data provided by maps.google.com" ATTR_STREET_NUMBER = 'Street Number' ATTR_STREET = 'Street' ATTR_CITY = 'City' ATTR_POSTAL_TOWN = 'Postal Town' ATTR_POSTAL_CODE = 'Postal Code' ATTR_REGION = 'State' ATTR_COUNTRY = 'Country' ATTR_COUNTY = 'County' ATTR_FORMATTED_ADDRESS = 'Formatted Address' DEFAULT_NAME = 'Google Geocode' DEFAULT_OPTION = 'street, city' DEFAULT_DISPLAY_ZONE = 'display' DEFAULT_KEY = 'no key' current = '0,0' zone_check = 'a' SCAN_INTERVAL = timedelta(seconds=60) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_ORIGIN): cv.string, vol.Optional(CONF_API_KEY, default=DEFAULT_KEY): cv.string, vol.Optional(CONF_OPTIONS, default=DEFAULT_OPTION): cv.string, vol.Optional(CONF_DISPLAY_ZONE, default=DEFAULT_DISPLAY_ZONE): cv.string, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_SCAN_INTERVAL, default=SCAN_INTERVAL): cv.time_period, }) TRACKABLE_DOMAINS = ['device_tracker', 'sensor'] def setup_platform(hass, config, add_devices, discovery_info=None): """Setup the sensor platform.""" name = config.get(CONF_NAME) api_key = config.get(CONF_API_KEY) origin = config.get(CONF_ORIGIN) options = config.get(CONF_OPTIONS) display_zone = config.get(CONF_DISPLAY_ZONE) add_devices([GoogleGeocode(hass, origin, name, api_key, options, display_zone)]) class GoogleGeocode(Entity): """Representation of a Google Geocode Sensor.""" def __init__(self, hass, origin, name, api_key, options, display_zone): """Initialize the sensor.""" self._hass = hass self._name = name self._api_key = api_key self._options = options.lower() self._display_zone = display_zone.lower() self._state = "Awaiting Update" self._street_number = None self._street = None self._city = None self._postal_town = None self._postal_code = None self._city = None self._region = None self._country = None self._county = None self._formatted_address = None self._zone_check_current = None # Check if origin is a trackable entity if origin.split('.', 1)[0] in TRACKABLE_DOMAINS: self._origin_entity_id = origin else: self._origin = origin @property def name(self): """Return the name of the sensor.""" return self._name @property def state(self): """Return the state of the sensor.""" return self._state @property def device_state_attributes(self): """Return the state attributes.""" return{ ATTR_STREET_NUMBER: self._street_number, ATTR_STREET: self._street, ATTR_CITY: self._city, ATTR_POSTAL_TOWN: self._postal_town, ATTR_POSTAL_CODE: self._postal_code, ATTR_REGION: self._region, ATTR_COUNTRY: self._country, ATTR_COUNTY: self._county, ATTR_ATTRIBUTION: CONF_ATTRIBUTION, ATTR_FORMATTED_ADDRESS: self._formatted_address, } @Throttle(SCAN_INTERVAL) def update(self): """Get the latest data and updates the states.""" if hasattr(self, '_origin_entity_id'): self._origin = self._get_location_from_entity( self._origin_entity_id ) """Update if location has changed.""" global current global zone_check_count global zone_check global user_display zone_check = self.hass.states.get(self._origin_entity_id).state zone_check_count = 2 if zone_check == self._zone_check_current: zone_check_count = 1 if zone_check == 'not_home': zone_check_count = 2 if zone_check_count == 1: pass elif self._origin == None: pass elif current == self._origin: pass else: _LOGGER.info("google request sent") self._zone_check_current = self.hass.states.get(self._origin_entity_id).state zone_check_count = 2 lat = self._origin current = lat self._reset_attributes() if self._api_key == 'no key': url = "https://maps.google.com/maps/api/geocode/json?latlng=" + lat else: url = "https://maps.googleapis.com/maps/api/geocode/json?latlng=" + lat + "&key=" + self._api_key response = get(url) json_input = response.text decoded = json.loads(json_input) street_number = '' street = 'Unnamed Road' alt_street = 'Unnamed Road' city = '' postal_town = '' formatted_address = '' state = '' county = '' country = '' for result in decoded["results"]: for component in result["address_components"]: if 'street_number' in component["types"]: street_number = component["long_name"] self._street_number = street_number if 'route' in component["types"]: street = component["long_name"] self._street = street if 'sublocality_level_1' in component["types"]: alt_street = component["long_name"] if 'postal_town' in component["types"]: postal_town = component["long_name"] self._postal_town = postal_town if 'locality' in component["types"]: city = component["long_name"] self._city = city if 'administrative_area_level_1' in component["types"]: state = component["long_name"] self._region = state if 'administrative_area_level_2' in component["types"]: county = component["long_name"] self._county = county if 'country' in component["types"]: country = component["long_name"] self._country = country if 'postal_code' in component["types"]: postal_code = component["long_name"] self._postal_code = postal_code if 'formatted_address' in decoded['results'][0]: formatted_address = decoded['results'][0]['formatted_address'] self._formatted_address = formatted_address if 'error_message' in decoded: self._state = decoded['error_message'] _LOGGER.error("You have exceded your daily requests plase create an api key.") elif self._display_zone == 'hide' or zone_check == "not_home": if street == 'Unnamed Road': street = alt_street self._street = alt_street if city == '': city = postal_town if city == '': city = county display_options = self._options user_display = [] if "street_number" in display_options: user_display.append(street_number) if "street" in display_options: user_display.append(street) if "city" in display_options: self._append_to_user_display(city) if "county" in display_options: self._append_to_user_display(county) if "state" in display_options: self._append_to_user_display(state) if "postal_code" in display_options: self._append_to_user_display(postal_code) if "country" in display_options: self._append_to_user_display(country) if "formatted_address" in display_options: self._append_to_user_display(formatted_address) user_display = ', '.join( x for x in user_display ) if user_display == '': user_display = street self._state = user_display else: self._state = zone_check[0].upper() + zone_check[1:] def _get_location_from_entity(self, entity_id): """Get the origin from the entity state or attributes.""" entity = self._hass.states.get(entity_id) if entity is None: _LOGGER.error("Unable to find entity %s", entity_id) return None # Check if the entity has origin attributes if location.has_location(entity): return self._get_location_from_attributes(entity) # When everything fails just return nothing return None def _reset_attributes(self): """Resets attributes.""" self._street = None self._street_number = None self._city = None self._postal_town = None self._postal_code = None self._region = None self._country = None self._county = None self._formatted_address = None def _append_to_user_display(self, append_check): """Appends attribute to state if false.""" if append_check == "": pass else: user_display.append(append_check) @staticmethod def _get_location_from_attributes(entity): """Get the lat/long string from an entities attributes.""" attr = entity.attributes return "%s,%s" % (attr.get(ATTR_LATITUDE), attr.get(ATTR_LONGITUDE))