diff --git a/README.md b/README.md index e36ed3c..fd59119 100644 --- a/README.md +++ b/README.md @@ -7,45 +7,68 @@ Show departure information from ctabustracker (Chicago Transit Authority). This plafrom is based of the findings of [@SilvrrGIT](https://github.com/SilvrrGIT) in this [forum post](https://community.home-assistant.io/t/cta-bus-tracker-sensor/92416) -**HA 0.86.0 or newer:** +## Installation +### HACS +Add this as a custom repository and restart your HA instance. +### Manual To get started put `/custom_components/ctabustracker/sensor.py` here: `/custom_components/ctabustracker/sensor.py` -**Older versions** - -To get started put `/custom_components/ctabustracker/sensor.py` here: -`/custom_components/sensor/ctabustracker.py` - ## Example configuration.yaml ```yaml sensor: platform: ctabustracker api_key: 'dfshkdkf7333ykgdfk73' + type: 'bus' lines: - - route: 151 - stop_id: 77 + - stop_id: 77 departures: 2 name: 'Union Station' + +sensor: + platform: ctabustracker + api_key: 'lk38vjklrj4nj' + type: 'train' + lines: + - stop_id: 3045 + departures: 2 + name: 'Logan Square' ``` -**Configuration variables:** +### Configuration variables key | type | description :--- | :--- | :--- **platform (Required)** | string | The platform name. -**api_key (Required)** | string | Your [API key](https://www.transitchicago.com/developers/bustracker/) +**api_key (Required)** | string | [CTA Bus API Key](https://www.transitchicago.com/developers/bustracker/) *or* [CTA Train API Key](https://www.transitchicago.com/developers/traintracker/) +**type** (Required) | string | Transit type: ["bus", "train"] **lines (Required)** | list | List of lines you want to track. +> **Note:** Bus times and train times each required a different API key. -**Lines configuration** +### Lines configuration key | type | description :--- | :--- | :--- -**route (Required)** | string | Route number (`rt`) **stop_id (Required)** | string | Stop ID (`stpid`) **departures (Optional)** | int | Number of future departures. -**name (Optional)** | list | List of lines you want to track. +**name (Optional)** | list | Name of the HA sensor + +## Change Log +### 0.0.3 +[@ludeeus](https://github.com/ludeeus): Initial release. + +### 0.0.3 (on HACS) +[@jonochocki](https://github.com/jonochocki): Added easy install via HACS support. + +### 0.0.4 +[@smcpeck](https://github.com/smcpeck): Added support for train lines. +- `route` config variable is no longer supported since the APIs don't need it was a "bus only" API parameter. *** +contributor | support +:--- | :--- +[@ludeeus](https://github.com/ludeeus) | [![BuyMeCoffee](https://camo.githubusercontent.com/cd005dca0ef55d7725912ec03a936d3a7c8de5b5/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6275792532306d6525323061253230636f666665652d646f6e6174652d79656c6c6f772e737667)](https://www.buymeacoffee.com/ludeeus) +[@smcpeck](https://github.com/smcpeck) | [![BuyMeCoffee](https://camo.githubusercontent.com/cd005dca0ef55d7725912ec03a936d3a7c8de5b5/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6275792532306d6525323061253230636f666665652d646f6e6174652d79656c6c6f772e737667)](https://www.buymeacoffee.com/shaunmcpeck) diff --git a/custom_components/ctabustracker/__init__.py b/custom_components/ctabustracker/__init__.py new file mode 100644 index 0000000..01c0f3f --- /dev/null +++ b/custom_components/ctabustracker/__init__.py @@ -0,0 +1 @@ +### diff --git a/custom_components/ctabustracker/manifest.json b/custom_components/ctabustracker/manifest.json new file mode 100644 index 0000000..8db2b2a --- /dev/null +++ b/custom_components/ctabustracker/manifest.json @@ -0,0 +1,9 @@ +{ + "domain": "ctabustracker", + "name": "CTA Bus Tracker", + "version": "0.0.4", + "documentation": "https://github.com/smcpeck/sensor.ctabustracker", + "dependencies": [], + "codeowners": ["@ludeeus","@jonochocki","@smcpeck"], + "requirements": [] + } diff --git a/custom_components/ctabustracker/sensor.py b/custom_components/ctabustracker/sensor.py index 0ba1c10..97fa904 100644 --- a/custom_components/ctabustracker/sensor.py +++ b/custom_components/ctabustracker/sensor.py @@ -2,9 +2,9 @@ Show departure information from ctabustracker (Chicago Transit Authority). For more details about this component, please refer to the documentation at -https://github.com/custom-components/sensor.ctabustracker/ +https://github.com/smcpeck/sensor.ctabustracker/ """ -from datetime import timedelta +import datetime as dt import logging import requests @@ -16,30 +16,32 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -__version__ = '0.0.3' +__version__ = '0.0.4' _LOGGER = logging.getLogger(__name__) CONF_API_KEY = 'api_key' CONF_DEPARTURES = 'departures' CONF_LINES = 'lines' -CONF_ROUTE = 'route' CONF_STOP_ID = 'stop_id' +CONF_TYPE = 'type' -RESOURCE = "http://ctabustracker.com/bustime/api/v2/" -ENDPOINT = "getpredictions?key={}&rt={}&stpid={}&format=json" +BUS_RESOURCE = "http://ctabustracker.com/bustime/api/v2/" +BUS_ENDPOINT = "getpredictions?key={}&stpid={}&format=json" +TRAIN_RESOURCE = "http://lapi.transitchicago.com/api/1.0/" +TRAIN_ENDPOINT = "ttarrivals.aspx?key={}&stpid={}&max={}&outputType=JSON" -TIME_BETWEEN_UPDATES = timedelta(seconds=60) +TIME_BETWEEN_UPDATES = dt.timedelta(seconds=60) LINES = vol.Schema({ vol.Required(CONF_STOP_ID): cv.string, - vol.Required(CONF_ROUTE): cv.string, vol.Optional(CONF_DEPARTURES, default=1): cv.positive_int, vol.Optional(CONF_NAME): cv.string, }) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_API_KEY): cv.string, + vol.Required(CONF_TYPE): vol.In(["bus", "train"]), vol.Required(CONF_LINES): vol.All(cv.ensure_list, [LINES]), }) @@ -49,40 +51,63 @@ async def async_setup_platform( """Set up the sensor platform.""" api_key = config[CONF_API_KEY] lines = config[CONF_LINES] + transit_type = config[CONF_TYPE] dev = [] for line in lines: - api = CtaBusData(api_key, line) + api = None + if transit_type == "bus": + api = CtaBusData(api_key, line) + elif transit_type == "train": + api = CtaTrainData(api_key, line) + for departure in range(0, line['departures']): - dev.append(CtaBusSensor(api, departure, line)) + dev.append(CtaSensor(api, transit_type, departure, line)) + async_add_entities(dev, True) -class CtaBusSensor(Entity): +class CtaSensor(Entity): """Representation of a Home Assistant sensor.""" - def __init__(self, api, departure, config): + def __init__(self, api, transit_type, departure, config): """Initialize the sensor.""" self.api = api self.departure = departure self.config = config self._state = None + self.transit_type = transit_type postfix = '' if departure == 0 else str(departure) self._name = "{} {}".format( - self.config.get('name', 'CTA '+self.config['route']), postfix) + self.config.get('name', 'CTA ' + self.config['stop_id']), postfix) def update(self): """Get the latest information.""" try: self.api.update() data = self.api.data + _LOGGER.debug(f"[{self.transit_type}] SENSOR.update() data = {data}") + if data: - self._state = data[self.departure].get('prdctdn') + if self.transit_type == "bus": + self._state = data[self.departure].get('prdctdn') + elif self.transit_type == "train": + prediction = data[self.departure] + pred_time = dt.datetime.strptime(prediction["prdt"], "%Y-%m-%dT%H:%M:%S") + arr_time = dt.datetime.strptime(prediction["arrT"], "%Y-%m-%dT%H:%M:%S") + minutes_left = int((arr_time-pred_time).total_seconds()/60) + if minutes_left <= 1: + minutes_left = "DUE" + self._state = minutes_left + else: + self._state = "Bad type" + _LOGGER.warning("Bad transit_type configured") else: - self._state = self._state - except Exception: # pylint: disable=W0703 + self._state = "No data" + _LOGGER.warning("No CTA data") + except Exception as ex: # pylint: disable=W0703 self._state = None - _LOGGER.debug(self._state) + _LOGGER.error(ex) @property def name(self): @@ -97,8 +122,36 @@ def state(self): @property def icon(self): """Set sensor icon.""" - return 'mdi:bus-clock' + return {'bus':'mdi:bus-clock','train':'mdi:train'}[self.transit_type] + +class CtaTrainData: + """Get the latest data and update the states.""" + + def __init__(self, api_key, config): + """Initialize the data object.""" + self.api_key = api_key + self.config = config + self.api = "{}{}".format( + TRAIN_RESOURCE, TRAIN_ENDPOINT.format( + self.api_key, self.config['stop_id'], self.config['departures'])) + self._data = None + + @Throttle(TIME_BETWEEN_UPDATES) + def update(self): + """Get the latest data from ctabustracker.""" + try: + self._data = requests.get( + self.api).json().get('ctatt', {}).get('eta', {}) + except Exception as error: # pylint: disable=W0703 + _LOGGER.error(error) + self._data = self._data + return self._data + @property + def data(self): + """Holds data.""" + return self._data + class CtaBusData: """Get the latest data and update the states.""" @@ -108,8 +161,8 @@ def __init__(self, api_key, config): self.api_key = api_key self.config = config self.api = "{}{}".format( - RESOURCE, ENDPOINT.format( - self.api_key, self.config['route'], self.config['stop_id'])) + BUS_RESOURCE, BUS_ENDPOINT.format( + self.api_key, self.config['stop_id'])) self._data = None @Throttle(TIME_BETWEEN_UPDATES) @@ -118,10 +171,10 @@ def update(self): try: self._data = requests.get( self.api).json().get('bustime-response', {}).get('prd', {}) - _LOGGER.debug(self._data) except Exception as error: # pylint: disable=W0703 _LOGGER.error(error) self._data = self._data + @property def data(self): diff --git a/hacs.json b/hacs.json new file mode 100644 index 0000000..bbfb9d1 --- /dev/null +++ b/hacs.json @@ -0,0 +1,5 @@ +{ + "name": "CTA Bus Tracker", + "render_readme": true, + "domains": ["sensor"], +} diff --git a/resources.json b/resources.json new file mode 100644 index 0000000..f849cd8 --- /dev/null +++ b/resources.json @@ -0,0 +1,4 @@ +[ + "https://raw.githubusercontent.com/smcpeck/sensor.ctabustracker/master/custom_components/ctabustracker/__init__.py", + "https://raw.githubusercontent.com/smcpeck/sensor.ctabustracker/master/custom_components/ctabustracker/manifest.json" +]