Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

14 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Portascan

Portascan runs on macOS, Linux, and Windows with Python 3.10 or newer. It checks Austrian BAUHAUS, OBI, and HORNBACH stores every 90 seconds. When a Midea PortaSplit becomes available, it opens the relevant orderable product page in the default browser. Amazon is not monitored.

Checks are intended to run Monday through Saturday from 08:00 to 20:00 local time. Starting the monitor outside those hours shows a “The stores are currently closed” dialog with “Continue anyway” and “Exit” options.

While the monitor runs, a blue cooling icon appears in the system tray or macOS menu bar. Its menu can stop the monitor. An availability alert also produces a native OS notification; clicking it opens the same product page in the default browser.

Requirements

  • Python 3.10 or newer
  • a default browser
  • a graphical desktop session for the tray icon, dialogs, and notifications
  • internet access to the official BAUHAUS, OBI, and HORNBACH websites

Clone the repository with Git or download and extract its ZIP archive from GitHub:

git clone https://github.com/tobomobo/Portascan.git
cd Portascan

Install on macOS and Linux

Create a virtual Python environment in the repository and install the runtime dependencies:

python3 --version
python3 -m venv .venv
.venv/bin/python -m pip install -r requirements.txt

If the launcher is not executable after downloading it:

chmod +x portascan

Linux also requires notify-send from libnotify 0.8 or newer, zenity or kdialog, and a working system tray. On Debian and Ubuntu, install the basic desktop helpers with:

sudo apt install python3 python3-venv libnotify-bin zenity

Depending on the desktop environment, its AppIndicator or GTK support may also need to be installed or enabled.

First test

Run one real availability check without opening a browser or sending an alert:

./portascan check

Then simulate an OBI Baden result. The orderable product page and a clickable native notification should appear:

./portascan simulate

Simulation does not change the saved real availability state.

Normal operation

./portascan start                  # start in the background
./portascan status                 # check whether it is running
tail -f PortaSplitMonitor.log      # follow live checks
./portascan stop                   # stop cleanly

Optionally start the monitor when signing in:

./portascan install-autostart
./portascan uninstall-autostart

This installs a LaunchAgent on macOS or a systemd user service on Linux. Do not move the repository afterward without reinstalling autostart.

Install on Windows

Install Python 3.10 or newer with the Python launcher (py). Open Command Prompt in the extracted repository and run:

py -3 --version
py -3 -m venv .venv
.venv\Scripts\python -m pip install -r requirements.txt

First test and normal operation

Double-click these files in Explorer or run them from Command Prompt:

  1. Monitor-testen.cmd performs a real check without opening a browser.
  2. Test.cmd simulates three available units at OBI Baden.
  3. Monitor-starten.cmd starts the monitor in the background.
  4. Monitor-stoppen.cmd stops it cleanly.
  5. Autostart-installieren.cmd enables start at sign-in.
  6. Autostart-entfernen.cmd removes autostart.

Check status and follow the log from PowerShell:

.\.venv\Scripts\python.exe .\portasplit_monitor.py status
Get-Content .\PortaSplitMonitor.log -Wait

Files and troubleshooting

The first completed check creates PortaSplitMonitor.config.json with default settings. These historic data-file names are retained so existing installations keep their configuration, state, and logs after upgrading to Portascan.

  • PortaSplitMonitor.log: checks, results, and errors
  • PortaSplitMonitor.state.json: last available and already alerted stock
  • PortaSplitMonitor.metrics.json: HTTP requests, rate limits, blocks, and network errors
  • PortaSplitMonitor.stores.json: cached HORNBACH store coordinates
  • data/austrian_postal_codes.json: bundled offline coordinates for 2,501 Austrian postal codes

If the monitor does not start, check its status and recent log entries:

./portascan status
tail -n 50 PortaSplitMonitor.log

Windows uses the same files. If Linux does not support clickable notifications, the monitor still opens the product page immediately and records the missing notification support in the log.

What happens when stock is found

The default browser opens an orderable product page and a clickable native notification appears. OBI and HORNBACH links select the reported store. BAUHAUS does not provide a shareable store-selected product URL, so its PortaSplit product page opens and the store named in the notification must be selected there.

Clicking the notification opens the same page again. If several stores report new stock in one check, each store gets its own browser page and notification. The default browser decides whether those appear as windows or tabs. The monitor continues checking every 90 seconds. By default, it alerts only once while stock remains continuously available; it can alert again after that stock disappears and later returns.

Notification click handling belongs to the running monitor process. A previously delivered notification can no longer react after the monitor is stopped, although the product page was already opened when the alert first appeared. simulate keeps its click handler alive for up to 30 seconds.

HTTP 429 responses and explicit throttling signals on 403 or 503 responses are logged as rate limits and delay the next request for at least the requested interval. PortaSplitMonitor.metrics.json records HTTP attempts, successful responses, HTTP errors, rate limits, plain 403 blocks, unexpected non-JSON responses, server errors, network errors, and rate-limit headers. This keeps website bot protection visible as a separate failure. Ambiguous combined rate-limit headers are recorded for diagnosis but do not create a cooldown without an explicit throttling signal.

All three retailers are queried directly and independently. OBI provides the store directory and accepts stock requests in batches of at most ten stores. BAUHAUS and HORNBACH each provide one aggregate product/store request per interval. The monitor has no runtime intermediary or dependency on letzteklima.com. A temporarily delisted HORNBACH product counts as no HORNBACH stock rather than stale availability. Amazon is neither queried nor parsed.

Settings

Edit PortaSplitMonitor.config.json with a text editor:

  • CheckIntervalSeconds: polling interval; minimum and default 90 seconds
  • PreferredStoreNumbers: preferred store numbers or qualified retailer keys, such as ["obi:039", "bauhaus:758"]; preferred available stores open first. Legacy bare OBI numbers such as "039" still work
  • OpenBrowserOnlyOncePerAvailability: when true, alert only once for continuously available stock at a store
  • NotificationPostalCode: optional four-digit Austrian postal code to resolve once into latitude and longitude
  • NotificationCenterLatitude, NotificationCenterLongitude, and NotificationRadiusKm: optional notification radius

Example: resolve postal code 1010 locally and use a 40 km radius:

{
  "CheckIntervalSeconds": 90,
  "PreferredStoreNumbers": [],
  "OpenBrowserOnlyOncePerAvailability": true,
  "NotificationPostalCode": "1010",
  "NotificationCenterLatitude": null,
  "NotificationCenterLongitude": null,
  "NotificationRadiusKm": 40
}

At the next check or start, the monitor reads the postal code from its bundled Austrian coordinate cache, writes the corresponding latitude and longitude into the same file, and resets NotificationPostalCode to null. Entering another postal code deliberately replaces the saved center. NotificationRadiusKm must be set when resolving a postal code.

Coordinates can still be entered manually. Without NotificationPostalCode, latitude, longitude, and radius must be configured together. Omitting all location values keeps nationwide monitoring. With a radius, OBI stock requests include only matching stores. The aggregate BAUHAUS and HORNBACH responses still require only one request each and are filtered locally before state or alerts are produced. HORNBACH coordinates are fetched from the official store page only for newly available stores and then cached locally. simulate deliberately bypasses the filter so the Baden test always works.

Distance is calculated locally as a great-circle distance. Postal codes and manually configured coordinates never leave the machine. The bundled postal-code centers are derived from the GeoNames postal-code dataset under CC BY 4.0. Road routes are longer than straight-line distances, so choose a conservative radius for a strict maximum driving distance.

The operating system must have a default browser. If a retailer changes its public interface, the error appears in the log and the monitor retries during the next interval.

Development

Install development tools and run the complete local quality gate:

.venv/bin/python -m pip install -r requirements-dev.txt
make check

GitHub Actions runs the same checks with Python 3.10 on macOS, Linux, and Windows. Product boundaries and direction are documented in VISION.md. Instructions for Codex and Claude are in AGENTS.md.

License

Portascan is available under the MIT License.

Meme

Midea congratulating its top PortaSplit salesman

About

Portascan — cross-platform Midea PortaSplit availability monitor for BAUHAUS, OBI, and HORNBACH stores in Austria.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages