From 4fdbbc497d59eacf863590fec535ec73f6fc0515 Mon Sep 17 00:00:00 2001 From: JC Connell Date: Thu, 22 Feb 2018 02:47:15 -0500 Subject: [PATCH] Python spotcrime (#12460) * Added python-spotcrime sensor * Added python-spotcrime sensor * Remove accidental included file * Update indentation to match PEP8 * Remove days from const.py * Changed default days to 1 * Incorporate changes requested by MartinHjelmare * Remove unnecessary import and fix lint error --- .coveragerc | 1 + homeassistant/components/sensor/spotcrime.py | 123 +++++++++++++++++++ requirements_all.txt | 3 + 3 files changed, 127 insertions(+) create mode 100644 homeassistant/components/sensor/spotcrime.py diff --git a/.coveragerc b/.coveragerc index 6c34f4d95ed..bd99e3ac2e2 100644 --- a/.coveragerc +++ b/.coveragerc @@ -629,6 +629,7 @@ omit = homeassistant/components/sensor/sochain.py homeassistant/components/sensor/sonarr.py homeassistant/components/sensor/speedtest.py + homeassistant/components/sensor/spotcrime.py homeassistant/components/sensor/steam_online.py homeassistant/components/sensor/supervisord.py homeassistant/components/sensor/swiss_hydrological_data.py diff --git a/homeassistant/components/sensor/spotcrime.py b/homeassistant/components/sensor/spotcrime.py new file mode 100644 index 00000000000..169bcc5f867 --- /dev/null +++ b/homeassistant/components/sensor/spotcrime.py @@ -0,0 +1,123 @@ +""" +Sensor for Spot Crime. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/sensor.spotcrime/ +""" + +from datetime import timedelta +from collections import defaultdict +import logging + +import voluptuous as vol + +from homeassistant.components.sensor import PLATFORM_SCHEMA +from homeassistant.const import ( + CONF_INCLUDE, CONF_EXCLUDE, CONF_NAME, CONF_LATITUDE, CONF_LONGITUDE, + ATTR_ATTRIBUTION, ATTR_LATITUDE, ATTR_LONGITUDE, CONF_RADIUS) +from homeassistant.helpers.entity import Entity +from homeassistant.util import slugify +import homeassistant.helpers.config_validation as cv + +REQUIREMENTS = ['spotcrime==1.0.2'] + +_LOGGER = logging.getLogger(__name__) + +CONF_DAYS = 'days' +DEFAULT_DAYS = 1 +NAME = 'spotcrime' + +EVENT_INCIDENT = '{}_incident'.format(NAME) + +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_DAYS, default=DEFAULT_DAYS): cv.positive_int, + vol.Optional(CONF_INCLUDE): vol.All(cv.ensure_list, [cv.string]), + vol.Optional(CONF_EXCLUDE): vol.All(cv.ensure_list, [cv.string]) +}) + + +# pylint: disable=unused-argument +def setup_platform(hass, config, add_devices, 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[CONF_NAME] + radius = config[CONF_RADIUS] + days = config.get(CONF_DAYS) + include = config.get(CONF_INCLUDE) + exclude = config.get(CONF_EXCLUDE) + + add_devices([SpotCrimeSensor( + name, latitude, longitude, radius, include, + exclude, days)], True) + + +class SpotCrimeSensor(Entity): + """Representation of a Spot Crime Sensor.""" + + def __init__(self, name, latitude, longitude, radius, + include, exclude, days): + """Initialize the Spot Crime sensor.""" + import spotcrime + self._name = name + self._include = include + self._exclude = exclude + self.days = days + self._spotcrime = spotcrime.SpotCrime( + (latitude, longitude), radius, None, None, self.days) + self._attributes = None + self._state = None + self._previous_incidents = set() + self._attributes = { + ATTR_ATTRIBUTION: spotcrime.ATTRIBUTION + } + + @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): + data = { + 'type': incident.get('type'), + 'timestamp': incident.get('timestamp'), + 'address': incident.get('location') + } + if incident.get('coordinates'): + data.update({ + ATTR_LATITUDE: incident.get('lat'), + ATTR_LONGITUDE: incident.get('lon') + }) + self.hass.bus.fire(EVENT_INCIDENT, data) + + def update(self): + """Update device state.""" + incident_counts = defaultdict(int) + incidents = self._spotcrime.get_incidents() + 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 (self._previous_incidents and incident.get('id') + not in self._previous_incidents): + self._incident_event(incident) + self._previous_incidents.add(incident.get('id')) + self._attributes.update(incident_counts) + self._state = len(incidents) diff --git a/requirements_all.txt b/requirements_all.txt index 9d498ef2749..112b77846fa 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1138,6 +1138,9 @@ somecomfort==0.5.0 # homeassistant.components.sensor.speedtest speedtest-cli==2.0.0 +# homeassistant.components.sensor.spotcrime +spotcrime==1.0.2 + # homeassistant.components.recorder # homeassistant.scripts.db_migrator # homeassistant.components.sensor.sql