"""Sensor for Crime Reports.""" from collections import defaultdict from datetime import timedelta import logging import crimereports import voluptuous as vol from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import ( ATTR_ATTRIBUTION, ATTR_LATITUDE, ATTR_LONGITUDE, CONF_EXCLUDE, CONF_INCLUDE, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME, CONF_RADIUS, LENGTH_KILOMETERS, LENGTH_METERS, ) import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity from homeassistant.util import slugify from homeassistant.util.distance import convert from homeassistant.util.dt import now _LOGGER = logging.getLogger(__name__) DOMAIN = "crimereports" EVENT_INCIDENT = f"{DOMAIN}_incident" SCAN_INTERVAL = timedelta(minutes=30) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( { vol.Required(CONF_NAME): cv.string, vol.Required(CONF_RADIUS): vol.Coerce(float), vol.Inclusive(CONF_LATITUDE, "coordinates"): cv.latitude, vol.Inclusive(CONF_LONGITUDE, "coordinates"): cv.longitude, vol.Optional(CONF_INCLUDE): vol.All(cv.ensure_list, [cv.string]), vol.Optional(CONF_EXCLUDE): vol.All(cv.ensure_list, [cv.string]), } ) def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Crime Reports platform.""" latitude = config.get(CONF_LATITUDE, hass.config.latitude) longitude = config.get(CONF_LONGITUDE, hass.config.longitude) name = config.get(CONF_NAME) radius = config.get(CONF_RADIUS) include = config.get(CONF_INCLUDE) exclude = config.get(CONF_EXCLUDE) add_entities( [CrimeReportsSensor(hass, name, latitude, longitude, radius, include, exclude)], True, ) class CrimeReportsSensor(Entity): """Representation of a Crime Reports Sensor.""" def __init__(self, hass, name, latitude, longitude, radius, include, exclude): """Initialize the Crime Reports sensor.""" self._hass = hass self._name = name self._include = include self._exclude = exclude radius_kilometers = convert(radius, LENGTH_METERS, LENGTH_KILOMETERS) self._crimereports = crimereports.CrimeReports( (latitude, longitude), radius_kilometers ) self._attributes = None self._state = None self._previous_incidents = set() @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 self._attributes def _incident_event(self, incident): """Fire if an event occurs.""" data = { "type": incident.get("type"), "description": incident.get("friendly_description"), "timestamp": incident.get("timestamp"), "location": incident.get("location"), } if incident.get("coordinates"): data.update( { ATTR_LATITUDE: incident.get("coordinates")[0], ATTR_LONGITUDE: incident.get("coordinates")[1], } ) self._hass.bus.fire(EVENT_INCIDENT, data) def update(self): """Update device state.""" incident_counts = defaultdict(int) incidents = self._crimereports.get_incidents( now().date(), include=self._include, exclude=self._exclude ) fire_events = len(self._previous_incidents) > 0 if len(incidents) < len(self._previous_incidents): self._previous_incidents = set() for incident in incidents: incident_type = slugify(incident.get("type")) incident_counts[incident_type] += 1 if fire_events and incident.get("id") not in self._previous_incidents: self._incident_event(incident) self._previous_incidents.add(incident.get("id")) self._attributes = {ATTR_ATTRIBUTION: crimereports.ATTRIBUTION} self._attributes.update(incident_counts) self._state = len(incidents)