From c0663bf7221a9b2e14a97f6de9c721cca7c7833f Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Thu, 10 Aug 2017 17:27:49 +0200 Subject: [PATCH] Add Shodan sensor (#8902) --- .coveragerc | 1 + homeassistant/components/sensor/shodan.py | 110 ++++++++++++++++++++++ requirements_all.txt | 3 + 3 files changed, 114 insertions(+) create mode 100644 homeassistant/components/sensor/shodan.py diff --git a/.coveragerc b/.coveragerc index 56ccb37d169..a18ec476010 100644 --- a/.coveragerc +++ b/.coveragerc @@ -496,6 +496,7 @@ omit = homeassistant/components/sensor/scrape.py homeassistant/components/sensor/sensehat.py homeassistant/components/sensor/serial_pm.py + homeassistant/components/sensor/shodan.py homeassistant/components/sensor/skybeacon.py homeassistant/components/sensor/sma.py homeassistant/components/sensor/snmp.py diff --git a/homeassistant/components/sensor/shodan.py b/homeassistant/components/sensor/shodan.py new file mode 100644 index 00000000000..c95d975ec47 --- /dev/null +++ b/homeassistant/components/sensor/shodan.py @@ -0,0 +1,110 @@ +""" +Sensor for displaying the number of result on Shodan.io. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/sensor.shodan/ +""" +import logging +from datetime import timedelta + +import voluptuous as vol + +import homeassistant.helpers.config_validation as cv +from homeassistant.components.sensor import PLATFORM_SCHEMA +from homeassistant.const import ATTR_ATTRIBUTION, CONF_API_KEY, CONF_NAME +from homeassistant.helpers.entity import Entity + +REQUIREMENTS = ['shodan==1.7.4'] + +_LOGGER = logging.getLogger(__name__) + +ATTRIBUTION = "Data provided by Shodan" + +CONF_QUERY = 'query' + +DEFAULT_NAME = 'Shodan Sensor' + +ICON = 'mdi:tooltip-text' + +SCAN_INTERVAL = timedelta(minutes=15) + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_API_KEY): cv.string, + vol.Required(CONF_QUERY): cv.string, + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, +}) + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """Set up the Shodan sensor.""" + import shodan + + api_key = config.get(CONF_API_KEY) + name = config.get(CONF_NAME) + query = config.get(CONF_QUERY) + + data = ShodanData(shodan.Shodan(api_key), query) + try: + data.update() + except shodan.exception.APIError as error: + _LOGGER.warning("Unable to connect to Shodan.io: %s", error) + return False + + add_devices([ShodanSensor(data, name)], True) + + +class ShodanSensor(Entity): + """Representation of the Shodan sensor.""" + + def __init__(self, data, name): + """Initialize the Shodan sensor.""" + self.data = data + self._name = name + self._state = None + self._unit_of_measurement = 'Hits' + + @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 unit_of_measurement(self): + """Return the unit the value is expressed in.""" + return self._unit_of_measurement + + @property + def icon(self): + """Return the icon to use in the frontend, if any.""" + return ICON + + @property + def device_state_attributes(self): + """Return the state attributes of the sensor.""" + return { + ATTR_ATTRIBUTION: ATTRIBUTION, + } + + def update(self): + """Get the latest data and updates the states.""" + self.data.update() + self._state = self.data.details['total'] + + +class ShodanData(object): + """Get the latest data and update the states.""" + + def __init__(self, api, query): + """Initialize the data object.""" + self._api = api + self._query = query + self.details = None + + def update(self): + """Get the latest data from shodan.io.""" + self.details = self._api.count(self._query) diff --git a/requirements_all.txt b/requirements_all.txt index f7ca7146dec..91be5eb7be3 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -871,6 +871,9 @@ sense-hat==2.2.0 # homeassistant.components.media_player.aquostv sharp_aquos_rc==0.3.2 +# homeassistant.components.sensor.shodan +shodan==1.7.4 + # homeassistant.components.alarm_control_panel.simplisafe simplisafe-python==1.0.4