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.
- 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 PortascanCreate 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.txtIf the launcher is not executable after downloading it:
chmod +x portascanLinux 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 zenityDepending on the desktop environment, its AppIndicator or GTK support may also need to be installed or enabled.
Run one real availability check without opening a browser or sending an alert:
./portascan checkThen simulate an OBI Baden result. The orderable product page and a clickable native notification should appear:
./portascan simulateSimulation does not change the saved real availability state.
./portascan start # start in the background
./portascan status # check whether it is running
tail -f PortaSplitMonitor.log # follow live checks
./portascan stop # stop cleanlyOptionally start the monitor when signing in:
./portascan install-autostart
./portascan uninstall-autostartThis installs a LaunchAgent on macOS or a systemd user service on Linux. Do not move the repository afterward without reinstalling autostart.
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.txtDouble-click these files in Explorer or run them from Command Prompt:
Monitor-testen.cmdperforms a real check without opening a browser.Test.cmdsimulates three available units at OBI Baden.Monitor-starten.cmdstarts the monitor in the background.Monitor-stoppen.cmdstops it cleanly.Autostart-installieren.cmdenables start at sign-in.Autostart-entfernen.cmdremoves autostart.
Check status and follow the log from PowerShell:
.\.venv\Scripts\python.exe .\portasplit_monitor.py status
Get-Content .\PortaSplitMonitor.log -WaitThe 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 errorsPortaSplitMonitor.state.json: last available and already alerted stockPortaSplitMonitor.metrics.json: HTTP requests, rate limits, blocks, and network errorsPortaSplitMonitor.stores.json: cached HORNBACH store coordinatesdata/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.logWindows 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.
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.
Edit PortaSplitMonitor.config.json with a text editor:
CheckIntervalSeconds: polling interval; minimum and default 90 secondsPreferredStoreNumbers: 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 workOpenBrowserOnlyOncePerAvailability: whentrue, alert only once for continuously available stock at a storeNotificationPostalCode: optional four-digit Austrian postal code to resolve once into latitude and longitudeNotificationCenterLatitude,NotificationCenterLongitude, andNotificationRadiusKm: 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.
Install development tools and run the complete local quality gate:
.venv/bin/python -m pip install -r requirements-dev.txt
make checkGitHub 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.
Portascan is available under the MIT License.
