Repository navigation
Ops Vendor Lib Updates
The mower adapters talk to real mowers through one vendor library each, pinned exactly in the adapter's pyproject.toml:
| Library | Adapter | Licence of the adapter |
|---|---|---|
pymammotion |
libs/mowdeck-control-mammotion |
GPL-3.0 |
pyworxcloud |
libs/mowdeck-control-worx |
GPL-3.0 |
aioautomower |
libs/mowdeck-control-husqvarna |
Apache-2.0 |
A bug in a new release can break a command, in the worst case the emergency stop or a plain stop. So the owner tests every update on real hardware first and only then merges it by hand. Nothing merges a vendor library update automatically.
-
Dependabot opens one pull request per new release (
.github/dependabot.yml, uv entry,fix(deps)prefix). The vendor libraries are not ignored and not grouped, so each release is visible on its own. The uv entry allows ten open pull requests, enough for all three vendor PRs to wait for their test alongside the routine updates. Dependabot proposes the newest release, not every release in between. -
vendor-lib-label.ymllabels the PRvendor-libandneeds-hardware-testwhen the PR is opened or reopened. This applies to any PR whoseuv.lockchanges the version of a vendor library, Dependabot's included. A PR that only adds a new adapter with its library is no update and gets no label. -
The check
vendor-lib / hardware test(vendor-lib-guard.yml) fails while the PR updates a vendor library and either carriesneeds-hardware-testor has novendor-liblabel yet. Its job summary lists the updated libraries and the next steps. Every other PR passes at once, so the check can be a required check without holding back routine updates. -
CI runs the adapters' unit tests against the new release: the
pythonjob installs the PR'suv.lockand runs every test underlibs/. That includes the controller path of each adapter withEmergencyStop,PauseMow,ReturnToDockandStartMowagainst the fake vendor client built from the library's real data classes. A red test there is a regression in the library or in the adapter's use of it. Fix the adapter in the same PR or close the PR. The Mammotion adapter also records the release it was tested against (PYMAMMOTION_VERSIONinmowdeck_control_mammotion/__init__.py), andtest_mammotion_package_is_gpl_and_pins_pymammotionfails until that constant matches the pin. Update the constant together with the test result, after the hardware test. -
dependabot-auto-merge.ymlnever approves or merges it (exclude-dependencies: pymammotion,pyworxcloud,aioautomower, also inside grouped PRs).
- Check out the PR branch on the machine that reaches the mower (
gh pr checkout <number>,uv sync), or deploy it to the test stack. - Run the checklist below for the adapter of the updated library, safety commands first. If a safety command fails, stop there.
-
Passed: write the result into the PR body (library version, mower model and firmware, date, the rows of the checklist). For pymammotion, also raise
PYMAMMOTION_VERSION(see above). Remove the labelneeds-hardware-testand keepvendor-lib. The check runs again on the label change and passes. Then approve and merge by hand, or enable auto-merge after the approval, in which casedependabot-auto-merge.ymlonly keeps the branch up to date. - Failed: keep the label, write what failed into the PR body and close the PR. Dependabot then skips this release and proposes the next one. If the adapter needs a change for the new release, make it in a separate PR that bumps the pin as well.
Removing the label is the record that the hardware test passed. Never remove it without the test. A rebase or a branch update does not put the label back; only reopening the PR does.
Test outdoors on the owner's mower, with the vendor app at hand as a fallback. Each adapter lists only the commands it supports. Rows marked "safety" must pass before anything else is tried.
| # | Step | Expected | Safety |
|---|---|---|---|
| 1 |
EmergencyStop while mowing |
wheels and blades stop at once (pause, zero movement, blades off) | yes |
| 2 |
PauseMow while mowing, then ResumeMow
|
work mode pause, then working again | yes |
| 3 | Manual drive (cloud, and BLE if bridged): drive, then let go of the stick; then close the app or drop the network mid-drive | the mower stops on release (the controller's dead-man stop() sends a zero frame) and after the frame ttl (watchdog); it never keeps driving |
yes |
| 4 |
ReturnToDock, and ReturnToDock(end_job=True)
|
returning, then docked or charging; with end_job the job does not resume |
yes |
| 5 |
StartMow of one area |
route accepted, mowing starts, progress reported | |
| 6 | State while docked and while mowing | activity, battery, charging, RTK status and source, position (the dock pose while docked), firmware | |
| 7 | Lift the mower briefly while it stands (blades off) | a lift or tilt fault, raw.lifted, theft alarm notification |
|
| 8 | Error code mapping: a fault the mower reports (for example the lift above, or a trapped mower) | code, English text and category as before; the error lexicon still has every code (test_mammotion_lexicon passes) |
|
| 9 | Owner script uv run python libs/mowdeck-control-mammotion/scripts/try_real_mower.py
|
connects, prints capabilities, state and the map |
| # | Step | Expected | Safety |
|---|---|---|---|
| 1 |
EmergencyStop while mowing |
the mower pauses at once, blades and wheels stop (the cloud has no hard stop) | yes |
| 2 |
PauseMow, then ResumeMow
|
status pause (34), then mowing again | yes |
| 3 | ReturnToDock |
going home, then home or charging | yes |
| 4 |
StartMow with now, a zone:<n> (if offered) and edge (if supported) |
mowing starts at that zone, or the edge cut starts | |
| 5 | State | activity, battery, charging, Wi-Fi signal, firmware, rain sensor, GPS position (models with GPS or RTK) | |
| 6 | Lift the mower or turn it over (blades off) | codes 2 or 11, raw.lifted, theft alarm notification |
|
| 7 | Error code mapping (a rain delay, a lift, a trapped mower) | Worx code, title and category as before | |
| 8 |
StartMow or ResumeMow while the mower reports an error; EmergencyStop while the cloud reports it offline |
start and resume refused (SensorStop); the emergency stop is still sent |
yes |
No manual driving: the Worx cloud has no such command.
| # | Step | Expected | Safety |
|---|---|---|---|
| 1 |
EmergencyStop while mowing |
the mower pauses at once (the Automower Connect API has no hard stop) | yes |
| 2 |
PauseMow, then ResumeMow (the schedule) |
paused, then mowing per schedule | yes |
| 3 |
ReturnToDock (park) |
going home, then parked or charging | yes |
| 4 |
StartMow by plan (a duration, a work area if the model has them) |
mowing starts | |
| 5 | State over the websocket | activity, battery, error, GPS position (models with GPS), derived blade state; the refresh stays within the API limits (no 429) | |
| 6 | Lift the mower (blades off) | lift codes (14, 15, 71, 131), raw.lifted, theft alarm notification |
|
| 7 | Error code mapping | code, text and category as before; test_husqvarna_lexicon passes (the copy of aioautomower's table equals the new release's) |
No manual driving: the Automower Connect API has no such command.
A new adapter declares its library in [tool.mowdeck.adapter] vendor-modules. Add it to VENDOR_LIBS in tools/repo-checks/scripts/vendor_lib_guard.py, to exclude-dependencies in .github/workflows/dependabot-auto-merge.yml and to the tables on this page. tools/repo-checks/tests/test_dependabot.py fails until the first two match.
Mirrored from docs/ops/vendor-lib-updates.md at commit ba48b2a269d43d4a393e2a0262758d8c20880aeb. Edit the file in the repository, not this wiki page.
Mirrored from the docs/ directory at commit ba48b2a269d43d4a393e2a0262758d8c20880aeb. Edit the files in the repository, not this wiki.
MowDeck documentation
- Home
- Phases
- Architecture
-
Decisions (ADR)
- Architecture decisions
- ADR 0001: OpenFGA instead of self-built authorization
- ADR 0002: Self-built Identity service in FastAPI instead of Keycloak or Zitadel
- ADR 0003: SvelteKit for the web app
- ADR 0004: Monorepo with a uv workspace
- ADR 0005: AGPL-3.0 for services and apps, Apache-2.0 for libraries
- ADR 0006: PostgreSQL for all persistence, no SQLite
- ADR 0007: NATS JetStream as event bus
- ADR 0008: Traefik as gateway
- ADR 0009: Command and event envelopes, subject naming and de-duplication
- ADR 0010: Python 3.13 for all services and libraries
- ADR 0011: Separate GPL-3.0 package for the PyMammotion adapter
- ADR 0012: Mower control interface and safety rules
- ADR 0013: Device-Gateway registry, sharding and per-mower concurrency
- ADR 0014: Authorization model, tuple ownership and the mowdeck-authz client
- ADR 0015: Tokens, sessions and key rotation
- ADR 0016: Fleet commands and authorization per command kind
- ADR 0017: Telemetry storage and retention
- ADR 0018: Realtime WebSocket protocol
- ADR 0019: Mammotion adapter transports, manual drive and secrets
- ADR 0020: Web app architecture
- ADR 0021: Browser authentication flow
- ADR 0022: Notifications: rules, recipients and delivery
- ADR 0023: Map rendering and projection
- ADR 0024: Scheduler: recurrence, rain pause and command issuing
- ADR 0025: Home Assistant integration as a pure API client
- ADR 0026: Manual drive channel over the Realtime WebSocket
- ADR 0027: Health history and theft alarm
- ADR 0028: Entitlements: plans as data and enforcement
- ADR 0029: Helm chart and release pipeline
- ADR 0030: Mowing patterns as plug-ins and the Planner
- ADR 0031: Billing with a Stripe adapter
- ADR 0032: Live camera over Agora, joined directly by the browser
- ADR 0033: Vision-assisted recovery with human approval
- ADR 0034: Linked cloud accounts and device discovery
- ADR 0035: Server-side camera frames and automatic AI analysis on errors
- ADR 0036: Native app shells with Capacitor, loading the web app from the server
- ADR 0037: Camera panorama calibration in the browser, with the AI supplying points only
- ADR 0038: Dual link per mower and firmware updates
- ADR 0039: Spot mowing by MowDeck-driven paths
- ADR 0040: Link heartbeat for online status
- ADR 0041: Spot runs from the dock: undock, transit and return
- ADR 0042: Drive calibration
- ADR 0043: Turn styles
- ADR 0044: Drive view with camera
- ADR 0045: Camera relay
- ADR 0046: Device settings as capabilities
- ADR 0047: The RTK base station as a Fleet device
- ADR 0048: Maintenance commands
- ADR 0049: Manual driving over the Mammotion cloud
- ADR 0050: Passkeys and the app login hand-off
- ADR 0051: Sign in with GitHub
- ADR 0052: Sign in with Apple
- ADR 0053: Retention and downsampling of the position track
- ADR 0054: Aerial photo under the mower map
- ADR 0055: Deploy maintenance window
- ADR 0056: Camera photo in push notifications for errors and theft alarms
- ADR 0057: Step-up re-authentication for critical actions
- ADR 0058: Theft tracking mode
- ADR 0059: iOS widgets and the mowing Live Activity
- ADR 0060: Apple Watch app
- ADR 0061: Mowing angle rotation
- ADR 0062: Setup tour for new users
- ADR 0063: Matter server (mowers in Apple Home and Google Home)
- ADR 0064: Personal access tokens and the public API documentation
- ADR 0065: Security center and new-login alerts
- ADR 0066: Cloud provider plugins and adapters for more vendors
- ADR 0067: Error code lexicon, provider independent
- ADR 0068: Public demo mode
- ADR 0069: Provider conformance kit and provider package template
- ADR 0070: Virtual zones for mowers without a vendor map
- ADR 0071: Capability matrix generated from the adapters
- ADR 0072: API explorer in the web app, for the public demo
-
Development
- Getting started
- Service template
- Shared libraries
- Cloud providers: adding a vendor
- Adding a provider: template and conformance kit
- Adding an error lexicon for a vendor
- Design: identity and authorization
- Design: fleet, telemetry, realtime
- Design: web app
- Web app
- Home Assistant integration
- Camera source bridge protocol
- Fuzzing and property-based tests
- GitHub wiki mirror
-
Operations
- Operating the Compose stack
- Operating MowDeck on Kubernetes (Helm)
- Building the Bluetooth bridge (ESPHome on a XIAO ESP32-S3)
- Releases
- Vendor library updates
- Billing operations
- Telemetry: retention and position downsampling
- iPhone and Android apps
- Passkeys
- Sign in with GitHub
- Aerial photo under the map
- Sign in with Apple
- Theft tracking mode
- Matter server
- Source code offer (AGPL-3.0 section 13)
- Public demo
- Spikes
-
Guides
- Guides
- ESP32 bridge: purchase and setup
- Signing in with a passkey
- Signing in with GitHub
- Error codes in plain words
- Aerial photo under the map
- Signing in with Apple
- Confirming critical actions
- Your security page
- iPhone widgets and the mowing Live Activity
- Apple Watch
- The setup tour
- Apple Home and Google Home (Matter)
- Worx Landroid, Kress and LandXcape mowers
- Scripting MowDeck
- Capability matrix
- API reference
- Original plan (German)