diff --git a/.coveragerc b/.coveragerc index cb5299beaad..de93cac55b5 100644 --- a/.coveragerc +++ b/.coveragerc @@ -714,6 +714,7 @@ omit = homeassistant/components/tikteck/light.py homeassistant/components/tile/device_tracker.py homeassistant/components/time_date/sensor.py + homeassistant/components/tmb/sensor.py homeassistant/components/todoist/calendar.py homeassistant/components/todoist/const.py homeassistant/components/tof/sensor.py diff --git a/CODEOWNERS b/CODEOWNERS index 93b41c6d38c..69ed17cbdf2 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -337,6 +337,7 @@ homeassistant/components/threshold/* @fabaff homeassistant/components/tibber/* @danielhiversen homeassistant/components/tile/* @bachya homeassistant/components/time_date/* @fabaff +homeassistant/components/tmb/* @alemuro homeassistant/components/todoist/* @boralyl homeassistant/components/toon/* @frenck homeassistant/components/tplink/* @rytilahti diff --git a/homeassistant/components/tmb/__init__.py b/homeassistant/components/tmb/__init__.py new file mode 100644 index 00000000000..5a0f5aa54fb --- /dev/null +++ b/homeassistant/components/tmb/__init__.py @@ -0,0 +1 @@ +"""Support for Transports Metropolitans de Barcelona.""" diff --git a/homeassistant/components/tmb/manifest.json b/homeassistant/components/tmb/manifest.json new file mode 100644 index 00000000000..bb76b3193fc --- /dev/null +++ b/homeassistant/components/tmb/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "tmb", + "name": "Transports Metropolitans de Barcelona", + "documentation": "https://www.home-assistant.io/integrations/tmb", + "requirements": [ + "tmb==0.0.4" + ], + "dependencies": [], + "codeowners": [ + "@alemuro" + ] +} \ No newline at end of file diff --git a/homeassistant/components/tmb/sensor.py b/homeassistant/components/tmb/sensor.py new file mode 100644 index 00000000000..6d8bdc7eac7 --- /dev/null +++ b/homeassistant/components/tmb/sensor.py @@ -0,0 +1,120 @@ +"""Support for TMB (Transports Metropolitans de Barcelona) Barcelona public transport.""" +from datetime import timedelta +import logging + +from requests import HTTPError +from tmb import IBus +import voluptuous as vol + +from homeassistant.components.sensor import PLATFORM_SCHEMA +from homeassistant.const import ATTR_ATTRIBUTION, CONF_NAME +import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.entity import Entity +from homeassistant.util import Throttle + +_LOGGER = logging.getLogger(__name__) + +ATTRIBUTION = "Data provided by Transport Metropolitans de Barcelona" + +ICON = "mdi:bus-clock" + +CONF_APP_ID = "app_id" +CONF_APP_KEY = "app_key" +CONF_LINE = "line" +CONF_BUS_STOP = "stop" +CONF_BUS_STOPS = "stops" +ATTR_BUS_STOP = "stop" +ATTR_LINE = "line" + +MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60) + +LINE_STOP_SCHEMA = vol.Schema( + { + vol.Required(CONF_BUS_STOP): cv.string, + vol.Required(CONF_LINE): cv.string, + vol.Optional(CONF_NAME): cv.string, + } +) + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( + { + vol.Required(CONF_APP_ID): cv.string, + vol.Required(CONF_APP_KEY): cv.string, + vol.Required(CONF_BUS_STOPS): vol.All(cv.ensure_list, [LINE_STOP_SCHEMA]), + } +) + + +def setup_platform(hass, config, add_entities, discovery_info=None): + """Set up the sensors.""" + ibus_client = IBus(config[CONF_APP_ID], config[CONF_APP_KEY]) + + sensors = [] + + for line_stop in config.get(CONF_BUS_STOPS): + line = line_stop[CONF_LINE] + stop = line_stop[CONF_BUS_STOP] + if line_stop.get(CONF_NAME): + name = f"{line} - {line_stop[CONF_NAME]} ({stop})" + else: + name = f"{line} - {stop}" + sensors.append(TMBSensor(ibus_client, stop, line, name)) + + add_entities(sensors, True) + + +class TMBSensor(Entity): + """Implementation of a TMB line/stop Sensor.""" + + def __init__(self, ibus_client, stop, line, name): + """Initialize the sensor.""" + self._ibus_client = ibus_client + self._stop = stop + self._line = line.upper() + self._name = name + self._unit = "minutes" + self._state = None + + @property + def name(self): + """Return the name of the sensor.""" + return self._name + + @property + def icon(self): + """Return the icon for the frontend.""" + return ICON + + @property + def unit_of_measurement(self): + """Return the unit of measurement.""" + return self._unit + + @property + def unique_id(self): + """Return a unique, HASS-friendly identifier for this entity.""" + return f"{self._stop}_{self._line}" + + @property + def state(self): + """Return the next departure time.""" + return self._state + + @property + def device_state_attributes(self): + """Return the state attributes of the last update.""" + return { + ATTR_ATTRIBUTION: ATTRIBUTION, + ATTR_BUS_STOP: self._stop, + ATTR_LINE: self._line, + } + + @Throttle(MIN_TIME_BETWEEN_UPDATES) + def update(self): + """Get the next bus information.""" + try: + self._state = self._ibus_client.get_stop_forecast(self._stop, self._line) + except HTTPError: + _LOGGER.error( + "Unable to fetch data from TMB API. Please check your API keys are valid." + ) diff --git a/requirements_all.txt b/requirements_all.txt index dd88fbe406e..f41f3456ba5 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1965,6 +1965,9 @@ thingspeak==1.0.0 # homeassistant.components.tikteck tikteck==0.4 +# homeassistant.components.tmb +tmb==0.0.4 + # homeassistant.components.todoist todoist-python==8.0.0