├── custom_components └── rozkladzik │ ├── __init__.py │ ├── manifest.json │ └── sensor.py ├── .github ├── FUNDING.yml └── workflows │ ├── hassfest.yaml │ ├── hacs.yaml │ ├── automerge.yaml │ └── release.yaml ├── hacs.json ├── LICENSE └── README.md /custom_components/rozkladzik/__init__.py: -------------------------------------------------------------------------------- 1 | """Rozkładzik""" -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | ko_fi: piotrmachowski 2 | custom: ["buycoffee.to/piotrmachowski", "paypal.me/PiMachowski", "revolut.me/314ma"] 3 | -------------------------------------------------------------------------------- /hacs.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Rozkładzik sensor", 3 | "domains": ["sensor"], 4 | "country": "PL", 5 | "render_readme": true, 6 | "zip_release": true, 7 | "filename": "rozkladzik.zip" 8 | } -------------------------------------------------------------------------------- /.github/workflows/hassfest.yaml: -------------------------------------------------------------------------------- 1 | name: Validate with hassfest 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | validate: 9 | runs-on: "ubuntu-latest" 10 | steps: 11 | - uses: "actions/checkout@v2" 12 | - uses: home-assistant/actions/hassfest@master 13 | -------------------------------------------------------------------------------- /custom_components/rozkladzik/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "domain": "rozkladzik", 3 | "name": "Rozkładzik", 4 | "documentation": "https://github.com/PiotrMachowski/Home-Assistant-custom-components-Rozkladzik", 5 | "issue_tracker": "https://github.com/PiotrMachowski/Home-Assistant-custom-components-Rozkladzik/issues", 6 | "dependencies": [], 7 | "codeowners": ["@PiotrMachowski"], 8 | "requirements": ["requests"], 9 | "version": "v1.1.5", 10 | "iot_class": "cloud_polling" 11 | } -------------------------------------------------------------------------------- /.github/workflows/hacs.yaml: -------------------------------------------------------------------------------- 1 | name: Validate HACS 2 | on: 3 | push: 4 | pull_request: 5 | jobs: 6 | ci: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | name: Download repo 11 | with: 12 | fetch-depth: 0 13 | 14 | - uses: actions/setup-python@v2 15 | name: Setup Python 16 | with: 17 | python-version: '3.8.x' 18 | 19 | - uses: actions/cache@v2 20 | name: Cache 21 | with: 22 | path: | 23 | ~/.cache/pip 24 | key: custom-component-ci 25 | 26 | - name: HACS Action 27 | uses: hacs/action@main 28 | with: 29 | CATEGORY: integration -------------------------------------------------------------------------------- /.github/workflows/automerge.yaml: -------------------------------------------------------------------------------- 1 | name: 'Automatically merge master -> dev' 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | build: 10 | name: Automatically merge master to dev 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | name: Git checkout 15 | with: 16 | fetch-depth: 0 17 | - name: Merge master -> dev 18 | run: | 19 | git config user.name "GitHub Actions" 20 | git config user.email "PiotrMachowski@users.noreply.github.com" 21 | if (git checkout dev) 22 | then 23 | git merge --ff-only master || git merge --no-commit master 24 | git commit -m "Automatically merge master -> dev" || echo "No commit needed" 25 | git push origin dev 26 | else 27 | echo "No dev branch" 28 | fi -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | release: 9 | name: Prepare release 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Download repo 13 | uses: actions/checkout@v1 14 | 15 | - name: Zip rozkladzik dir 16 | run: | 17 | cd /home/runner/work/Home-Assistant-custom-components-Rozkladzik/Home-Assistant-custom-components-Rozkladzik/custom_components/rozkladzik 18 | zip rozkladzik.zip -r ./ 19 | 20 | - name: Upload zip to release 21 | uses: svenstaro/upload-release-action@v1-release 22 | with: 23 | repo_token: ${{ secrets.GITHUB_TOKEN }} 24 | file: /home/runner/work/Home-Assistant-custom-components-Rozkladzik/Home-Assistant-custom-components-Rozkladzik/custom_components/rozkladzik/rozkladzik.zip 25 | asset_name: rozkladzik.zip 26 | tag: ${{ github.ref }} 27 | overwrite: true -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Piotr Machowski 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![HACS Default][hacs_shield]][hacs] 2 | [![GitHub Latest Release][releases_shield]][latest_release] 3 | [![GitHub All Releases][downloads_total_shield]][releases] 4 | [![Ko-Fi][ko_fi_shield]][ko_fi] 5 | [![buycoffee.to][buycoffee_to_shield]][buycoffee_to] 6 | [![PayPal.Me][paypal_me_shield]][paypal_me] 7 | [![Revolut.Me][revolut_me_shield]][revolut_me] 8 | 9 | 10 | 11 | [hacs_shield]: https://img.shields.io/static/v1.svg?label=HACS&message=Default&style=popout&color=green&labelColor=41bdf5&logo=HomeAssistantCommunityStore&logoColor=white 12 | [hacs]: https://hacs.xyz/docs/default_repositories 13 | 14 | [latest_release]: https://github.com/PiotrMachowski/Home-Assistant-custom-components-Rozkladzik/releases/latest 15 | [releases_shield]: https://img.shields.io/github/release/PiotrMachowski/Home-Assistant-custom-components-Rozkladzik.svg?style=popout 16 | 17 | [releases]: https://github.com/PiotrMachowski/Home-Assistant-custom-components-Rozkladzik/releases 18 | [downloads_total_shield]: https://img.shields.io/github/downloads/PiotrMachowski/Home-Assistant-custom-components-Rozkladzik/total 19 | 20 | 21 | # Rozkładzik sensor 22 | 23 | This sensor uses unofficial API to get data from [*Rozkładzik.pl*](https://www.rozkladzik.pl) and provide information about departures for chosen stop. 24 | 25 | ## Configuration options 26 | 27 | | Key | Type | Required | Default | Description | 28 | | --- | --- | --- | --- | --- | 29 | | `name` | `string` | `False` | `Rozkładzik` | Name of sensor | 30 | | `city` | `string` | `True` | - | Name of city used in API | 31 | | `stops` | `list` | `True` | - | List of stops to monitor | 32 | 33 | ### Configuration for stop 34 | 35 | | Key | Description | 36 | | --- | --- | 37 | | `id` | ID of stop | 38 | | `name` | Name of stop | 39 | | `stops_group_mode` | Enables stops group mode. Possible values: `true`, `false`. | 40 | | `lines` | `list` | `False` | all available | List of monitored lines. | 41 | | `directions` | `list` | `False` | all available | List of monitored directions. | 42 | 43 | ## Example usage 44 | 45 | ``` 46 | sensor: 47 | - platform: rozkladzik 48 | city: 'wroclaw' 49 | stops: 50 | - id: 1281 51 | name: 'Plac Grunwaldzki' 52 | directions: 53 | - "Reja" 54 | - id: 94 55 | name: 'Rynek' 56 | stops_group_mode: true 57 | lines: 58 | - "33" 59 | ``` 60 | 61 | ## Installation 62 | 63 | ### Using [HACS](https://hacs.xyz/) (recommended) 64 | 65 | This integration can be installed using HACS. 66 | To do it search for `Rozkładzik` in *Integrations* section. 67 | 68 | ### Manual 69 | 70 | To install this integration manually you have to download [*rozkladzik.zip*](https://github.com/PiotrMachowski/Home-Assistant-custom-components-Rozkladzik/releases/latest/download/rozkladzik.zip) and extract its contents to `config/custom_components/rozkladzik` directory: 71 | ```bash 72 | mkdir -p custom_components/rozkladzik 73 | cd custom_components/rozkladzik 74 | wget https://github.com/PiotrMachowski/Home-Assistant-custom-components-Rozkladzik/releases/latest/download/rozkladzik.zip 75 | unzip rozkladzik.zip 76 | rm rozkladzik.zip 77 | ``` 78 | 79 | 80 | ## Hints 81 | 82 | * This sensor provides attributes which can be used in [*HTML card*](https://github.com/PiotrMachowski/Home-Assistant-Lovelace-HTML-card) or [*HTML Template card*](https://github.com/PiotrMachowski/Home-Assistant-Lovelace-HTML-Template-card): `html_timetable`, `html_departures` 83 | * HTML Card: 84 | ```yaml 85 | - type: custom:html-card 86 | title: 'Rozkładzik' 87 | content: | 88 |
Departures
89 | [[ sensor.rozkladzik_wroclaw_1709.attributes.html_departures ]] 90 |
Timetable
91 | [[ sensor.rozkladzik_wroclaw_1709.attributes.html_timetable ]] 92 | ``` 93 | * HTML Template Card: 94 | ```yaml 95 | - type: custom:html-template-card 96 | title: 'Rozkładzik' 97 | ignore_line_breaks: true 98 | content: | 99 |
Departures
100 | {{ state_attr('sensor.rozkladzik_wroclaw_1709','html_departures') }} 101 |
Timetable
102 | {{ state_attr('sensor.rozkladzik_wroclaw_1709','html_timetable') }} 103 | ``` 104 | * This integration is available in [*HACS*](https://github.com/custom-components/hacs/). 105 | ## FAQ 106 | 107 | * **How to get values for configuration parameters?** 108 | 109 | To find out values for configuration parameters follow the following steps: 110 | - Go to [rozkladzik.pl](https://www.rozkladzik.pl) and find desired stop. 111 | - Activate developer tools using `[F12]` button. 112 | - Click on chosen stop and in network tab look for call to `https://www.rozkladzik.pl//timetable.txt?...` URL 113 | - Value for `stops_group_mode` is determined by value of query parameter `c`. If it is equal to `bsa` you have to enable group mode. 114 | - Value for `city` comes from `` path fragment. 115 | - Value for `id` comes from query parameter `t` or `b` for group mode. 116 | 117 | 118 | 119 | 120 | ## Support 121 | 122 | If you want to support my work with a donation you can use one of the following platforms: 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 137 | 140 | 144 | 145 | 146 | 147 | 151 | 154 | 155 | 156 | 157 | 158 | 161 | 164 | 167 | 168 | 169 | 170 | 174 | 177 | 180 | 181 |
PlatformPayment methodsLinkComment
Ko-fi 134 |
  • PayPal
  • 135 |
  • Credit card
  • 136 |
    138 | Buy Me a Coffee at ko-fi.com 139 | 141 |
  • No fees
  • 142 |
  • Single or monthly payment
  • 143 |
    buycoffee.to 148 |
  • BLIK
  • 149 |
  • Bank transfer
  • 150 |
    152 | Postaw mi kawę na buycoffee.to 153 |
    PayPal 159 |
  • PayPal
  • 160 |
    162 | PayPal Logo 163 | 165 |
  • No fees
  • 166 |
    Revolut 171 |
  • Revolut
  • 172 |
  • Credit Card
  • 173 |
    175 | Revolut 176 | 178 |
  • No fees
  • 179 |
    182 | 183 | ### Powered by 184 | [![PyCharm logo.](https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.svg)](https://jb.gg/OpenSourceSupport) 185 | 186 | 187 | [ko_fi_shield]: https://img.shields.io/static/v1.svg?label=%20&message=Ko-Fi&color=F16061&logo=ko-fi&logoColor=white 188 | 189 | [ko_fi]: https://ko-fi.com/piotrmachowski 190 | 191 | [buycoffee_to_shield]: https://shields.io/badge/buycoffee.to-white?style=flat&labelColor=white&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABhmlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw1AUhU9TpaIVh1YQcchQnayIijhKFYtgobQVWnUweemP0KQhSXFxFFwLDv4sVh1cnHV1cBUEwR8QVxcnRRcp8b6k0CLGC4/3cd49h/fuA4R6malmxzigapaRisfEbG5FDLzChxB6MIZ+iZl6Ir2QgWd93VM31V2UZ3n3/Vm9St5kgE8knmW6YRGvE09vWjrnfeIwK0kK8TnxqEEXJH7kuuzyG+eiwwLPDBuZ1BxxmFgstrHcxqxkqMRTxBFF1ShfyLqscN7irJarrHlP/sJgXltOc53WEOJYRAJJiJBRxQbKsBClXSPFRIrOYx7+QcefJJdMrg0wcsyjAhWS4wf/g9+zNQuTE25SMAZ0vtj2xzAQ2AUaNdv+PrbtxgngfwautJa/UgdmPkmvtbTIEdC3DVxctzR5D7jcAQaedMmQHMlPSygUgPcz+qYcELoFulfduTXPcfoAZGhWSzfAwSEwUqTsNY93d7XP7d+e5vx+AIahcq//o+yoAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH5wETCy4vFNqLzwAAAVpJREFUOMvd0rFLVXEYxvHPOedKJnKJhrDLuUFREULE7YDCMYj+AydpsCWiaKu29hZxiP4Al4aWwC1EdFI4Q3hqEmkIBI8ZChWXKNLLvS0/Qcza84V3enm/7/s878t/HxGkeTaIGziP+EB918nawu7Dq1d0e1+2J2bepnk2jFEUVVF+qKV51o9neBCaugfge70keoxxUbSWjrQ+4SUyzKZ5NlnDZdzGG7w4DIh+dtZEFntDA98l8S0MYwctNGrYz9WqKJePFLq80g5Sr+EHlnATp+NA+4qLaZ7FfzMrzbMBjGEdq8GrJMZnvAvFC/8wfAwjWMQ8XmMzaW9sdevNRgd3MFhvNpbaG1u/Dk2/hOc4gadVUa7Um425qii/7Z+xH9O4jwW8Cqv24Tru4hyeVEU588cfBMgpPMI9nMFe0BkFzVOYrYqycyQgQJLwTC2cDZCPeF8V5Y7jGb8BUpRicy7OU5MAAAAASUVORK5CYII= 192 | 193 | [buycoffee_to]: https://buycoffee.to/piotrmachowski 194 | 195 | [buy_me_a_coffee_shield]: https://img.shields.io/static/v1.svg?label=%20&message=Buy%20me%20a%20coffee&color=6f4e37&logo=buy%20me%20a%20coffee&logoColor=white 196 | 197 | [buy_me_a_coffee]: https://www.buymeacoffee.com/PiotrMachowski 198 | 199 | [paypal_me_shield]: https://img.shields.io/static/v1.svg?label=%20&message=PayPal.Me&logo=paypal 200 | 201 | [paypal_me]: https://paypal.me/PiMachowski 202 | 203 | [revolut_me_shield]: https://img.shields.io/static/v1.svg?label=%20&message=Revolut&logo=revolut 204 | 205 | [revolut_me]: https://revolut.me/314ma 206 | -------------------------------------------------------------------------------- /custom_components/rozkladzik/sensor.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import datetime 3 | 4 | import voluptuous as vol 5 | 6 | from homeassistant.components.sensor import PLATFORM_SCHEMA, ENTITY_ID_FORMAT 7 | from homeassistant.const import CONF_NAME 8 | import homeassistant.helpers.config_validation as cv 9 | from homeassistant.helpers.entity import Entity 10 | from homeassistant.helpers.entity import async_generate_entity_id 11 | 12 | CONF_STOPS = 'stops' 13 | CONF_STOP_ID = 'id' 14 | CONF_STOP_NAME = 'name' 15 | CONF_GROUP_MODE = 'stops_group_mode' 16 | CONF_LINES = 'lines' 17 | CONF_DIRECTIONS = 'directions' 18 | CONF_CITY = 'city' 19 | 20 | DEFAULT_NAME = 'Rozkładzik' 21 | 22 | PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ 23 | vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, 24 | vol.Required(CONF_CITY): cv.string, 25 | vol.Required(CONF_STOPS): vol.All(cv.ensure_list, [ 26 | vol.Schema({ 27 | vol.Required(CONF_STOP_ID): cv.positive_int, 28 | vol.Required(CONF_STOP_NAME): cv.string, 29 | vol.Optional(CONF_GROUP_MODE, default=False): cv.boolean, 30 | vol.Optional(CONF_LINES, default=[]): cv.ensure_list, 31 | vol.Optional(CONF_DIRECTIONS, default=[]): cv.ensure_list 32 | })]) 33 | }) 34 | 35 | 36 | def setup_platform(hass, config, add_entities, discovery_info=None): 37 | name = config.get(CONF_NAME) 38 | city = config.get(CONF_CITY) 39 | stops = config.get(CONF_STOPS) 40 | dev = [] 41 | for stop in stops: 42 | stop_id = stop.get(CONF_STOP_ID) 43 | stop_name = stop.get(CONF_STOP_NAME) 44 | group_mode = stop.get(CONF_GROUP_MODE) 45 | lines = stop.get(CONF_LINES) 46 | directions = stop.get(CONF_DIRECTIONS) 47 | uid = '{}_{}_{}'.format(name, city, stop_id) 48 | entity_id = async_generate_entity_id(ENTITY_ID_FORMAT, uid, hass=hass) 49 | dev.append(RozkladzikSensor(entity_id, name, city, stop_id, stop_name, group_mode, lines, directions)) 50 | add_entities(dev, True) 51 | 52 | 53 | class RozkladzikSensor(Entity): 54 | def __init__(self, entity_id, name, city, stop_id, stop_name, group_mode, watched_lines, watched_directions): 55 | self.entity_id = entity_id 56 | self._name = name 57 | self._stop_id = stop_id 58 | self._stop_name = stop_name 59 | self._city = city 60 | self._city_data = self.get_city_data() 61 | self._group_mode = group_mode 62 | self._watched_lines = watched_lines 63 | self._watched_directions = watched_directions 64 | self._last_response = None 65 | self._departures_number = 0 66 | self._departures_ordered = [] 67 | self._departures_by_line = dict() 68 | self._state = None 69 | 70 | @property 71 | def name(self): 72 | return '{} {} - {}'.format(self._name, self._city, self._stop_name) 73 | 74 | @property 75 | def state(self): 76 | if self._departures_number is not None and self._departures_number > 0: 77 | return RozkladzikSensor.departure_to_str(self._departures_ordered[0]) 78 | return None 79 | 80 | @staticmethod 81 | def departure_to_str(departure): 82 | return '{} kier. {}: {} ({}m)'.format(departure[0], departure[1], departure[2], departure[3]) 83 | 84 | @property 85 | def unit_of_measurement(self): 86 | return None 87 | 88 | @property 89 | def extra_state_attributes(self): 90 | attr = dict() 91 | if self._departures_ordered is not None: 92 | attr['list'] = self._departures_ordered 93 | attr['html_timetable'] = self.get_html_timetable() 94 | attr['html'] = attr['html_timetable'] 95 | attr['html_departures'] = self.get_html_departures() 96 | if self._departures_number > 0: 97 | dep = self._departures_ordered[0] 98 | attr['line'] = dep[0] 99 | attr['direction'] = dep[1] 100 | attr['departure'] = dep[2] 101 | attr['time_to_departure'] = dep[3] 102 | return attr 103 | 104 | def update(self): 105 | now = datetime.datetime.now() 106 | r_time = now.hour * 60 + now.minute 107 | if self._should_update(r_time): 108 | url_template = 'https://www.rozkladzik.pl/{}/timetable.txt?c=tsa&t={}&day={}&time={}' 109 | if self._group_mode: 110 | url_template = 'https://www.rozkladzik.pl/{}/timetable.txt?c=bsa&b={}&day={}&time={}' 111 | response = requests.get(url_template.format(self._city, self._stop_id, now.weekday(), r_time)) 112 | if response.status_code == 200 and response.content.__len__() > 0: 113 | self._last_response = response.text 114 | if self._last_response is not None: 115 | self.update_values_for_time(r_time) 116 | 117 | def _should_update(self, now): 118 | return self._last_response is None or self._departures_ordered is None or len(self._departures_ordered) == 0 or self._departures_ordered[0][4] < now 119 | 120 | def update_values_for_time(self, r_time): 121 | raw_array = self._last_response.split("|") 122 | self._departures_ordered = [] 123 | self._departures_by_line = dict() 124 | departures_by_line = dict() 125 | lines_directions = dict() 126 | for r in raw_array: 127 | line, direction_number, departures = self.process_raw(r, r_time) 128 | direction = self.get_direction(line, direction_number) 129 | if len(self._watched_lines) > 0 and line not in self._watched_lines \ 130 | or len(self._watched_directions) > 0 and direction not in self._watched_directions: 131 | continue 132 | if not line in lines_directions: 133 | lines_directions[line] = [] 134 | lines_directions[line].append(direction) 135 | departures_by_line[(line, direction)] = [] 136 | for departure_time, departure_diff, departure_timestamp in departures: 137 | self._departures_ordered.append((line, direction, departure_time, departure_diff, departure_timestamp)) 138 | departures_by_line[(line, direction)].append((departure_time, departure_diff)) 139 | departures_by_line[(line, direction)].sort(key=lambda e: e[1]) 140 | for line in lines_directions: 141 | self._departures_by_line[line] = dict() 142 | for direction in lines_directions[line]: 143 | self._departures_by_line[line][direction] = [] 144 | for departure in departures_by_line[(line, direction)]: 145 | self._departures_by_line[line][direction].append(departure) 146 | self._departures_ordered.sort(key=lambda e: e[3]) 147 | self._departures_number = len(self._departures_ordered) 148 | 149 | def get_html_timetable(self): 150 | html = '\n' 151 | lines = list(self._departures_by_line.keys()) 152 | lines.sort() 153 | for line in lines: 154 | directions = list(self._departures_by_line[line].keys()) 155 | directions.sort() 156 | for direction in directions: 157 | if len(direction) == 0: 158 | continue 159 | html = html + ''.format(line, direction) 160 | departures = ', '.join(map(lambda x: x[0], self._departures_by_line[line][direction])) 161 | html = html + '\n'.format(departures) 162 | if len(lines) == 0: 163 | html = html + '' 164 | html = html + '
    {}, kier. {}{}
    Brak połączeń
    ' 165 | return html 166 | 167 | def get_html_departures(self): 168 | html = '\n' 169 | for departure in self._departures_ordered: 170 | html = html + '\n'.format( 171 | RozkladzikSensor.departure_to_str(departure)) 172 | html = html + '
    {}
    ' 173 | return html 174 | 175 | def get_city_data(self): 176 | url_template = 'https://www.rozkladzik.pl/{}/data.txt' 177 | response = requests.get(url_template.format(self._city)) 178 | data = response.text 179 | lines = data.split("#SEP#") 180 | stopNames = lines[0].split(";") 181 | linesData = lines[11].split("#!#") 182 | lineDefinitions = dict() 183 | for lineData in linesData: 184 | rows = lineData.split(";") 185 | lineName = rows[0] 186 | lineDirections = [] 187 | for i in range(0, len(rows) - 2, 5): 188 | directionId = int(rows[i + 2]) 189 | directionName = stopNames[directionId] 190 | stops = rows[i + 3].split("|") 191 | lineDirection = (directionId, directionName, stops) 192 | lineDirections.append(lineDirection) 193 | lineDefinition = (lineName, lineDirections) 194 | lineDefinitions[lineName] = lineDefinition 195 | return lineDefinitions 196 | 197 | def get_direction(self, line, direction_number): 198 | return self._city_data[line][1][direction_number][1] 199 | 200 | @staticmethod 201 | def process_raw(raw, now): 202 | raw = raw[:raw.find("#")] 203 | raw_split = raw.split(";") 204 | line = raw_split[0] 205 | direction_number = int(raw_split[1]) 206 | times = [] 207 | for i in range(3, len(raw_split), 4): 208 | time = int(raw_split[i]) 209 | diff = time - now 210 | if diff < 0: 211 | diff += 1440 212 | hour = time // 60 213 | minute = time % 60 214 | t = "{:02}:{:02}".format(hour, minute) 215 | times.append((t, diff, time)) 216 | return line, direction_number, times 217 | --------------------------------------------------------------------------------