Skip to content

Commit

Permalink
feat: Support multiple directions (#33)
Browse files Browse the repository at this point in the history
This allows specifying multiple directions by separating them with
commas. This allows to only see the directions you want to see.
E.g. only inwards town and along the Ring at Schönhauser Allee.

For each direction, the departures will be fetched individually and
collected together.

Since S41/S42 show up for both directions on the Ring, the departures
have to de-duplicated, for which I used a set and made Departure
hashable. The hash is calculated over all information exposed in
to_dict, since that's everything, which users have access to over the
sensor.
  • Loading branch information
Cornelicorn authored Mar 25, 2024
1 parent e01793b commit b743bdf
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 9 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,14 @@ sensor:
departures:
- name: "S+U Schönhauser Allee" # free-form name, only for display purposes
stop_id: 900110001 # actual Stop ID for the API
# direction: 900000100002 # Optional stop_id to limit departures for a specific direction (same URL as to find the stop_id)
# direction: 900110002,900007102 # Optional stop_id to limit departures for a specific direction (same URL as to find the stop_id), multiple Values can be specified using a comma separated list
# walking_time: 5 # Optional parameter with value in minutes that hides transport closer than N minutes
# suburban: false # Optionally hide transport options
# show_official_line_colors: true # Optionally enable official VBB line colors. By default predefined colors will be used.
# duration: 30 # Optional (default 10), query departures for how many minutes from now?
- name: "Stargarder Str." # currently you have to add more than one stop to track
stop_id: 900000110501
# direction: 900000100002 # Optional stop_id to limit departures for a specific direction (same URL as to find the stop_id)
# direction: 900000100002 # Optional stop_id to limit departures for a specific direction (same URL as to find the stop_id), multiple Values can be specified using a comma separated list
# walking_time: 5 # Optional parameter with value in minutes that hide transport closer than N minutes
# show_official_line_colors: true # Optionally enable official VBB line colors. By default predefined colors will be used.
# duration: 30 # Optional (default 10), query departures for how many minutes from now?
Expand Down
11 changes: 11 additions & 0 deletions custom_components/berlin_transport/departure.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,14 @@ def to_dict(self, show_api_line_colors: bool, walking_time: int):
"delay": self.delay,
"walking_time": walking_time,
}

# Make the object hashable and use all infos that can be displayed in the
# frontend
def __hash__(self):
# The value of colors and walking time doesn't matter, it just needs to
# be the same for all evaluations of this function
items = self.to_dict(show_api_line_colors=False, walking_time=0).items()
# Dictionaries are not hashable, so use the items, sort them for
# reproducibility. Convert it to a tuple, since lists are also not
# hashable
return hash(tuple(sorted(items)))
26 changes: 19 additions & 7 deletions custom_components/berlin_transport/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
"""The Berlin (BVG) and Brandenburg (VBB) transport integration."""
from __future__ import annotations
import logging
from typing import Optional
from datetime import datetime, timedelta

import requests
Expand Down Expand Up @@ -140,15 +139,15 @@ def extra_state_attributes(self):
def update(self):
self.departures = self.fetch_departures()

def fetch_departures(self) -> Optional[list[Departure]]:
def fetch_directional_departure(self, direction: str | None) -> list[Departure]:
try:
response = requests.get(
url=f"{API_ENDPOINT}/stops/{self.stop_id}/departures",
params={
"when": (
datetime.utcnow() + timedelta(minutes=self.walking_time)
).isoformat(),
"direction": self.direction,
"direction": direction,
"duration": self.duration,
"results": API_MAX_RESULTS,
"suburban": self.config.get(CONF_TYPE_SUBURBAN) or False,
Expand Down Expand Up @@ -179,10 +178,23 @@ def fetch_departures(self) -> Optional[list[Departure]]:
return []

# convert api data into objects
unsorted = [
Departure.from_dict(departure) for departure in departures.get("departures")
]
return sorted(unsorted, key=lambda d: d.timestamp)
return [Departure.from_dict(departure) for departure in departures.get("departures")]

def fetch_departures(self) -> list[Departure]:
departures = []

if self.direction is None:
departures += self.fetch_directional_departure(self.direction)
else:
for direction in self.direction.split(','):
departures += self.fetch_directional_departure(direction)

# Get rid of duplicates
# Duplicates should only exist for the Ringbahn and filtering for both
# directions
deduplicated_departures = set(departures)

return sorted(deduplicated_departures, key=lambda d: d.timestamp)

def next_departure(self):
if self.departures and isinstance(self.departures, list):
Expand Down

0 comments on commit b743bdf

Please sign in to comment.