Feuersoftware
Feuersoftware is a library that allow you to interact with the Feuersoftware Connect Public API.
All routes of the public API are implemented. Request bodies are passed as
plain dicts (or lists of dicts) and validated with pydantic before they are
sent, so invalid data raises a pydantic.ValidationError without hitting the API.
Only the fields you pass are sent.
Installation
pip install feuersoftware
Setup the API
from feuersoftware import FeuersoftwareAPI
TOKEN = '2xgRoQfoMGb4IveCDJIZqOO1l8hZZ5jT5mAw7SSk1otrFSq50IA2HIYB3luEpv7Vw8BWwG'\
'Y2zV96VUkOF3FCZs2OP03qaTWF3CDrUHOKndvLIFTTgx0FCMBTFBRF1DfG4g3rs8BSMHB4'\
'6qph1AlxOZ6parmJlp90V3GQB4EoI6DFdKE4SZeBuu46mXoaDlSmpTTS3FCpeG7oEUJVgy'\
'pLZkZSFPRng5HdKhp6HG2XmNIMAtKTG3DAUWuKRi3cZ4JstLj05y4r7jt81g4DYXz9gVYc'\
'UWk2pOkIZ9RPmu0s4LlaXHEK3TJlxLIUt5eHIzPUVKXyhdJDckviPsTYNfRxkpcNGd0vAb'\
'zfzwMadgb4xaOi1v6ZpsRfXyOPgpudcnO6rwwi9TlAWNZ2075CO7HVFEP31yGhXmYsdFwj'\
'ne3UIraWovMWHqeyv2yQLigKLePDAgXYUFqQpZ9P5ScznSMUg0ZnxS0Miy0qKe9zDYtqTk'\
'qQVwrUGfGVFp4Ti83NJLCCGUOCmF0ovOB28mYyQIqGAi2MDaNIuAvz6HT1tGAo5nYdzOeu'
api = FeuersoftwareAPI(TOKEN)
Return values
Every method returns the requests.Response of the call, or None if the
request could not be sent at all (connection error, timeout, ...). Failed
requests (non 2xx status codes) are logged to the Feuersoftware logger but
not raised, so check the response yourself:
r = api.get_vehicles()
if r is not None and r.ok:
vehicles = r.json()
OData queries
get_operations, get_appointments and get_vehicle_availabilities accept
the optional OData arguments filter, orderby, top and skip:
api.get_appointments(filter="End ge 2025-05-15", orderby="Start desc", top=10)
Enums
Fields that take an enum value accept plain integers or the IntEnums from
feuersoftware.models:
| Enum | Values |
|---|---|
AvailabilityStatus |
Available (0), LimitedAvailable (1), NotAvailable (2) |
AvailabilitySource |
None_ (0), Manual (1), Pager (2), Auto (3), Scheduled (4), ScheduledRecurring (5), UserApi (6) |
DefectReportStatus |
Reported (0), Reviewed (1), InProgress (2), Resolved (3), Rejected (4), Paused (5) |
DefectReportPriority |
Low (0), Medium (1), High (2) |
NewsCategory |
Announcement (0), TrafficObstruction (1), FasNotification (2) |
NewsType |
SiteNews (0), OrganizationNews (1) |
SiteInfoType |
PhoneNumber (0), EmailAddress (1), WebReference (2), DocumentReference (3), Text (4) |
UserOperationStatus |
NotSet (0), Coming (1), NotComing (2), ComingLater (3) |
VehiclePropertyType |
String (0), Integer (1), Double (2), Boolean (3), DateTime (4) |
VehicleUserPosition |
None_ (0), Leader (1), Driver (2) |
Endpoints
Alarm groups
| Method | HTTP |
|---|---|
get_alarmgroup() |
GET /alarmgroup |
put_alarmgroup(id, data) |
PUT /alarmgroup/{id} |
api.put_alarmgroup(42, {
"Name": "Vollalarm",
"Users": [{"Email": "jane@example.com", "UserName": "jane"}],
})
Appointments
| Method | HTTP |
|---|---|
get_appointments(filter, orderby, top, skip) |
GET /appointment |
Results are sorted by start date ascending, at most 1000 items are returned. To get current and future appointments filter by today's date:
api.get_appointments(filter="End ge 2025-05-15")
Billing
| Method | HTTP |
|---|---|
get_billing_accounts() |
GET /billing/account |
Defect reports
| Method | HTTP |
|---|---|
get_defect_reports() |
GET /defectReport |
post_defect_report(data) |
POST /defectReport |
get_defect_report(id) |
GET /defectReport/{id} |
put_defect_report(id, data) |
PUT /defectReport/{id} |
delete_defect_report(id) |
DELETE /defectReport/{id} |
get_defect_report_history(id) |
GET /defectReport/{id}/statusHistory |
post_defect_report_attachment(id, data) |
POST /defectReport/{id}/attach |
put_defect_report_attachment_attach(id, attachmentId, ok) |
PUT /defectReport/{id}/attach/{attachmentId} |
get_defect_report_attachment(attachmentId) |
GET /defectReport/attach/{attachmentId} |
put_defect_report_attachment(attachmentId, data) |
PUT /defectReport/attach/{attachmentId} |
delete_defect_report_attachment(attachmentId) |
DELETE /defectReport/attach/{attachmentId} |
get_defect_report_attachment_url(attachmentId) |
GET /defectReport/attach/url/{attachmentId} |
put_defect_report_attachment_abuse(attachmentId, data) |
PUT /defectReport/attachabuse/{attachmentId} |
from feuersoftware.models import DefectReportPriority, DefectReportStatus
api.post_defect_report({
"SiteId": 1,
"Status": DefectReportStatus.Reported,
"Priority": DefectReportPriority.High,
"ShortDescription": "Blaulicht defekt",
"DetailedDescription": "Das linke Blaulicht auf dem Dach blinkt nicht mehr.",
"VehicleId": 12345678,
})
Attachments are added in two steps: register the file metadata, then confirm
(ok=True) or discard (ok=False) the attachment:
api.post_defect_report_attachment(17, {
"Name": "foto.jpg",
"Size": 123456,
"MimeType": "image/jpeg",
})
api.put_defect_report_attachment_attach(17, attachmentId=99, ok=True)
get_defect_report_attachment follows the server redirect to the file, so the
response body is the file itself.
Defect report categories
| Method | HTTP |
|---|---|
get_defect_report_categories() |
GET /defectReportCategory |
post_defect_report_category(data) |
POST /defectReportCategory |
put_defect_report_category(id, data) |
PUT /defectReportCategory/{id} |
delete_defect_report_category(id) |
DELETE /defectReportCategory/{id} |
api.post_defect_report_category({"Name": "Fahrzeugtechnik", "SiteId": 1})
api.put_defect_report_category(3, {"Name": "Fahrzeuge"})
Diagnostics
| Method | HTTP |
|---|---|
post_diagnostics_upload_request(data, monitor_timestamp, monitor_signature) |
POST /diagnostics/upload-request |
monitor_timestamp and monitor_signature are sent as the
X-Monitor-Timestamp and X-Monitor-Signature headers if given.
api.post_diagnostics_upload_request({
"Message": "Monitor crashed",
"MonitorName": "Wache 1",
"MonitorVersion": "1.2.3",
"OsInfo": "Windows",
"OsVersion": "11",
})
Functions
| Method | HTTP |
|---|---|
get_functions() |
GET /function |
Geocoding
| Method | HTTP |
|---|---|
get_geocoding(address) |
GET /geocoding |
api.get_geocoding("Hauptstraße 1, 79761 Waldshut-Tiengen")
Infoboard
| Method | HTTP |
|---|---|
get_infoboard() |
GET /infoboard |
post_infoboard(data) |
POST /infoboard |
get_infoboard_info(id) |
GET /infoboard/{id} |
put_infoboard_info(id, data) |
PUT /infoboard/{id} |
delete_infoboard_info(id) |
DELETE /infoboard/{id} |
get_infoboard_groups() |
GET /infoboard/groups |
post_infoboard_group(data) |
POST /infoboard/groups |
put_infoboard_group(id, data) |
PUT /infoboard/groups/{id} |
from feuersoftware.models import SiteInfoType
api.post_infoboard_group({"Name": "Telefonnummern", "SiteId": 1})
api.post_infoboard({
"InfoGroupId": 5,
"Description": "Leitstelle",
"InfoType": SiteInfoType.PhoneNumber,
"Value": "+49 7751 12345",
})
Mailing lists
| Method | HTTP |
|---|---|
get_mailinglists() |
GET /mailinglists |
News
| Method | HTTP |
|---|---|
get_news() |
GET /news |
post_news(data, news_type) |
POST /news |
put_news(id, data) |
PUT /news/{id} |
delete_news(id) |
DELETE /news/{id} |
news_type defaults to NewsType.SiteNews on the server side.
from feuersoftware.models import NewsCategory, NewsType
api.post_news(
{
"Title": "Übung",
"Content": "Am Freitag findet eine Übung statt.",
"Start": "2025-05-15",
"End": "2025-05-16",
"Category": NewsCategory.Announcement,
},
news_type=NewsType.OrganizationNews,
)
Operations
| Method | HTTP |
|---|---|
get_operations(only_latest, filter, orderby, top, skip) |
GET /operation |
post_operation(data, update_strategy) |
POST /operation |
get_operation_message(id) |
GET /operation/{id}/message |
post_operation_message(id, data) |
POST /operation/{id}/message |
get_operation_assignment(id) |
GET /operation/{id}/assignment |
post_operation_assignment(id, data) |
POST /operation/{id}/assignment |
put_operation_assignment_crew(id, vehicle_id, data) |
PUT /operation/{id}/assignment/{vehicleId}/crew |
get_operation_assignment_users(id, vehicle_id) |
GET /operation/{id}/assignment/{vehicleId}/users |
post_operation_assignment_user(id, vehicle_id, data) |
POST /operation/{id}/assignment/{vehicleId}/users |
put_operation_assignment_users(id, vehicle_id, data) |
PUT /operation/{id}/assignment/{vehicleId}/users |
delete_operation_assignment_user(id, vehicle_id, user_assignment_id) |
DELETE /operation/{id}/assignment/{vehicleId}/users/{id} |
get_operation_user_status(id) |
GET /operation/{id}/userstatus |
post_operation_user_status(data) |
POST /operation/userstatus |
get_operation_documentation(id) |
GET /operation/{id}/documentation |
put_operation_documentation(id, data) |
PUT /operation/{id}/documentation |
Receive data about running operations
api.get_operations()
By default the server only returns operations of the last 4 hours. Pass
only_latest=False to get the operation history. At most 100 items are
returned per request, use top/skip to page through them:
api.get_operations(only_latest=False, orderby="Start desc", top=100, skip=100)
Start new operation
alarm_data = {
"Start": "2025-05-15T12:19:48.909Z",
"End": "2025-05-15T12:19:48.909Z",
"Status": 0,
"AlarmEnabled": True,
"Keyword": "string",
"Address": {
"Street": "string",
"HouseNumber": "string",
"ZipCode": "string",
"City": "string",
"District": "string"
},
"Reporter": {
"Name": "string",
"PhoneNumber": "string"
},
"Position": {
"Latitude": 0,
"Longitude": 0
},
"Facts": "string",
"Ric": "string",
"Number": "string",
"Source": "string",
"Properties": [
{
"Key": "string",
"Value": "string",
"Priority": 0
}
],
"AlarmedVehicles": [
{
"Id": 0,
"RadioIdentifier": "string"
}
],
"AssignedVehicles": [
{
"Name": "string",
"VehicleId": 0,
"RadioId": "string",
"Assigned": "2025-05-15T12:19:48.909Z",
"Alerted": "2025-05-15T12:19:48.909Z",
"Finished": "2025-05-15T12:19:48.909Z",
"Status1": "2025-05-15T12:19:48.909Z",
"Status2": "2025-05-15T12:19:48.909Z",
"Status3": "2025-05-15T12:19:48.909Z",
"Status4": "2025-05-15T12:19:48.909Z",
"Status7": "2025-05-15T12:19:48.909Z",
"Status8": "2025-05-15T12:19:48.909Z"
}
]
}
api.post_operation(alarm_data)
If you want to update a running operation, you can pass an argument to api.post_operation:
api.post_operation(alarm_data, update_strategy="byNumber")
update_strategy can be one of four strings: "none", "byNumber", "byAddress", "byPosition"
Operation messages
api.post_operation_message("4711", {
"MessageText": "Lage unter Kontrolle",
"Source": "ILS",
"SenderName": "Florian Waldshut 1/44-1",
})
Vehicle assignments
from feuersoftware.models import VehicleUserPosition
api.post_operation_assignment("4711", {"VehicleId": 12345678, "Source": "ILS"})
api.put_operation_assignment_crew("4711", "12345678", {
"Crew": 9,
"RespiratorCarriers": 4,
})
api.post_operation_assignment_user("4711", "12345678", {
"UserName": "jane",
"Position": VehicleUserPosition.Leader,
"IsRespiratorCarrier": False,
})
put_operation_assignment_users takes a list of user assignments and replaces
all user assignments of the vehicle.
User operation status
from feuersoftware.models import UserOperationStatus
api.post_operation_user_status({
"OperationId": 4711,
"PagerIssi": "1234567",
"Status": UserOperationStatus.Coming,
"TimeUntilArrival": {"Duration": 300, "Distance": 2500},
})
Operation documentation
api.put_operation_documentation("4711", {
"SituationOnArrival": "Rauchentwicklung aus Kellerfenster",
"Activity": "Brandbekämpfung mit 1 C-Rohr unter PA",
"Leader": "Max Mustermann",
})
Organization
| Method | HTTP |
|---|---|
get_organization() |
GET /organization |
Users
| Method | HTTP |
|---|---|
get_users() |
GET /user |
get_user(id) |
GET /user/{id} |
put_user(id, data) |
PUT /user/{id} |
patch_user(id, data) |
PATCH /user/{id} |
delete_user(id) |
DELETE /user/{id} |
post_user_invite(data) |
POST /user/invite |
put_user_availability(id, data) |
PUT /user/{id}/availability/current |
get_user_profilepicture(id) |
GET /user/{id}/profilepicture |
api.post_user_invite({
"Email": "jane@example.com",
"FirstName": "Jane",
"LastName": "Doe",
"Roles": [{"RoleName": "User"}],
})
patch_user takes a JSON Patch document, the
supported ops are add, remove and replace:
api.patch_user("abc123", [{"op": "replace", "path": "/lastName", "value": "Doe"}])
The id of put_user_availability can be the user id or the pager number:
from feuersoftware.models import AvailabilityStatus
api.put_user_availability("1234567", {
"Status": AvailabilityStatus.NotAvailable,
"Until": "2025-05-16T08:00:00Z",
"Info": "Urlaub",
})
User API
These endpoints are authenticated with a personal user token instead of the API token.
| Method | HTTP |
|---|---|
get_user_availablity(token, status, lifeTimeDays) |
GET /user/useravailability |
get_user_status(token, status, driveTimeSeconds, driveDistanceMeters, siteId) |
GET /user/userstatus |
api.get_user_availablity("usertoken", status=0, lifeTimeDays=1)
api.get_user_status(
"usertoken", status=1, driveTimeSeconds=300, driveDistanceMeters=2500, siteId=1
)
Vehicles
| Method | HTTP |
|---|---|
get_vehicles() |
GET /vehicle |
get_vehicle_image(id, identifier_preference) |
GET /vehicle/{id}/image |
get_vehicle_status(id, identifier_preference) |
GET /vehicle/{id}/status |
post_vehicle_status(id, data, identifier_preference) |
POST /vehicle/{id}/status |
id can be either the vehicle id or the radio id. The server resolves it
according to identifier_preference, which defaults to "PreferRadioId" on
the server side.
Set vehicle status
status_data = {
"Status": 3,
"Position": {
"Latitude": 47.59902386911071,
"Longitude": 8.334801219413004
},
"StatusTimestamp": "2025-05-15T12:24:08.905Z",
"PositionTimestamp": "2025-05-15T12:24:08.905Z",
"Source": "ILS"
}
api.post_vehicle_status(12345678, status_data)
Vehicle availability
| Method | HTTP |
|---|---|
get_vehicle_availabilities(id, filter, orderby, top, skip) |
GET /vehicle/{id}/availability |
post_vehicle_availability(id, data) |
POST /vehicle/{id}/availability |
put_vehicle_availability(id, availability_id, data) |
PUT /vehicle/{id}/availability/{availabilityId} |
delete_vehicle_availability(id, availability_id) |
DELETE /vehicle/{id}/availability/{availabilityId} |
get_vehicle_availabilities returns current and future availabilities,
sorted by start date ascending. VehicleId is filled in from id if it's
missing in data.
from feuersoftware.models import AvailabilityStatus
api.post_vehicle_availability(12345678, {
"Status": AvailabilityStatus.NotAvailable,
"Info": "TÜV",
"Start": "2025-05-20T07:00:00Z",
"End": "2025-05-20T16:00:00Z",
})
Vehicle CVM modules
| Method | HTTP |
|---|---|
get_vehicle_cvms(id) |
GET /vehicle/{id}/cvm |
post_vehicle_cvm(id, data) |
POST /vehicle/{id}/cvm |
get_vehicle_cvm(id, cvm_id) |
GET /vehicle/{id}/cvm/{cvmId} |
put_vehicle_cvm(id, cvm_id, data) |
PUT /vehicle/{id}/cvm/{cvmId} |
delete_vehicle_cvm(id, cvm_id) |
DELETE /vehicle/{id}/cvm/{cvmId} |
api.post_vehicle_cvm(12345678, {
"SerialNumber": "CVM-0001",
"Description": "HLF 20",
"OverrideDescription": False,
})
Vehicle properties
| Method | HTTP |
|---|---|
get_vehicle_properties(id) |
GET /vehicle/{id}/properties |
post_vehicle_properties(id, data) |
POST /vehicle/{id}/properties |
from feuersoftware.models import VehiclePropertyType
api.post_vehicle_properties(12345678, [
{"Key": "Tankinhalt", "Value": "75", "Unit": "%", "Type": VehiclePropertyType.Integer},
{"Key": "Kilometerstand", "Value": "48211", "Unit": "km"},
])
Wasserkarte
These endpoints live under /interfaces/wasserkarte instead of
/interfaces/public.
| Method | HTTP |
|---|---|
get_wasserkarte_active() |
GET /wasserkarte/active |
get_wasserkarte_hydrants(lat, lng, range, numItems) |
GET /wasserkarte/hydrant |
api.get_wasserkarte_hydrants(lat=47.599, lng=8.334, range=500, numItems=10)
Metadata
Release files for feuersoftware 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| feuersoftware-0.3.0.tar.gz | 27.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| feuersoftware-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 41.6 kB
Release files / feuersoftware-0.3.0.tar.gz
| Download URL | feuersoftware-0.3.0.tar.gz |
|---|---|
| Size | 27.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4597f418d82823791ff594c94356512938c97dc4e1e1750423ea9b87788ac2da
|
|
BLAKE2b-256 checksum How to use checksums |
5e4d84dc1c545eb53c96d54ccf8eaef668e97d4b9c567a2c952b4afd590741dd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 5, 2026.
Transparency logRelease files / feuersoftware-0.3.0-py3-none-any.whl
| Download URL | feuersoftware-0.3.0-py3-none-any.whl |
|---|---|
| Size | 14.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
dba7d67417152f22c0539312c665d8666fedb7c511fb859ad0ded0ef66e4bf95
|
|
BLAKE2b-256 checksum How to use checksums |
c4ac8fd31b65011d4deaf7cd77dab697249a6baff92642e44d2de81b16712f46
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 5, 2026.
Transparency log