Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
/build/
/dist/
docs/build/
kismet-web-docs/
10 changes: 8 additions & 2 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
Changelog
=========

v2025.03.13
-----------

Changes
~~~~~~~
- Allow passing fields/regex to Devices.all via POST payload.
- Switch datasource control endpoints (pause/resume/open/close) to POST to match current Kismet API.

v2020.05.01
-----------

Expand Down Expand Up @@ -37,5 +45,3 @@ Other
- Update docs. [Mike Kershaw / Dragorn]
- Start extracting module. [Mike Kershaw / Dragorn]
- Started repo. [Mike Kershaw / Dragorn]


36 changes: 36 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,42 @@ Installing from source
Usage examples
--------------

Authentication and setup
~~~~~~~~~~~~~~~~~~~~~~~~

Kismet now supports API tokens and roles. Use an API key with the ``admin`` role for controlling datasources, or a ``readonly`` key for queries. Pass it as ``apikey`` when creating an interface (basic auth via ``username`` / ``password`` also works).

Quickstart (modern API + API key):
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

::

from kismet_rest import Datasources, Devices

# Replace with your Kismet host and an API key (admin role for control)
ds = Datasources(host_uri="http://127.0.0.1:2501", apikey="YOUR_ADMIN_API_KEY")

# List datasources
for src in ds.all():
print(src["kismet.datasource.uuid"], src["kismet.datasource.name"])

# Ensure a datasource is running (required before devices will appear)
target_uuid = "<UUID_FROM_LIST>"
ds.open(target_uuid) # start it if it was not auto-started
ds.set_hop(target_uuid) # optional: enable hopping

# Fetch recently active devices
dev = Devices(host_uri="http://127.0.0.1:2501", apikey="YOUR_READONLY_OR_ADMIN_KEY")
for device in dev.all(ts=0, fields=["kismet.device.base.macaddr"]):
print(device.get("kismet.device.base.macaddr"))

Notes and troubleshooting
~~~~~~~~~~~~~~~~~~~~~~~~~

- Datasource control endpoints (pause/resume/close/open) require POST and the admin role in current Kismet releases.
- If no devices are returned, make sure at least one datasource is open/running; either configure it to auto-start in ``kismet.conf`` or call ``open(uuid)`` via the API.
- Device listing supports field simplification and regex filters; use the ``fields`` and ``regex`` kwargs with ``Devices.all`` to reduce response size.
- Tested against Kismet 2025-09-R1.

Legacy functionality (KismetConnector):
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
2 changes: 1 addition & 1 deletion kismet_rest/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@
from .system import System # NOQA
from .utility import Utility # NOQA

__version__ = "2023.01.01"
__version__ = "2025.03.13"
9 changes: 4 additions & 5 deletions kismet_rest/datasources.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ def pause(self, source):

"""
url = "/datasource/by-uuid/{}/pause_source.cmd".format(source)
return self.interact("GET", url, only_status=True)
return self.interact("POST", url, only_status=True)

def resume(self, source):
"""Resume paused source.
Expand All @@ -138,7 +138,7 @@ def resume(self, source):

"""
url = "/datasource/by-uuid/{}/resume_source.cmd".format(source)
return self.interact("GET", url, only_status=True)
return self.interact("POST", url, only_status=True)

def close(self, uuid):
"""Close source. A closed source will no longer be processed, and will remain closed unless reopened.
Expand All @@ -151,7 +151,7 @@ def close(self, uuid):

"""
url = "/datasource/by-uuid/{}/close_source.cmd".format(uuid)
return self.interact("GET", url, only_status=True)
return self.interact("POST", url, only_status=True)

def open(self, uuid):
"""Reopen a closed source.
Expand All @@ -164,5 +164,4 @@ def open(self, uuid):

"""
url = "/datasource/by-uuid/{}/open_source.cmd".format(uuid)
return self.interact("GET", url, only_status=True)

return self.interact("POST", url, only_status=True)
11 changes: 10 additions & 1 deletion kismet_rest/devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ def all(self, callback=None, callback_args=None, **kwargs):

Keyword args:
ts (int): Starting last-seen timestamp in seconds since Epoch.
fields (list): List of fields to return.
regex (list): Regex filters per Kismet command_param spec.

Yield:
dict: Device json, or None if callback is set.
Expand All @@ -29,10 +31,17 @@ def all(self, callback=None, callback_args=None, **kwargs):
callback_settings["callback"] = callback
if callback_args:
callback_settings["callback_args"] = callback_args
valid_payload = ["fields", "regex"]
payload = {kword: kwargs[kword] for kword in valid_payload
if kword in kwargs}
# Remove payload-only keys so they don't get formatted into the URL.
for kword in valid_payload:
kwargs.pop(kword, None)
query_args = self.kwargs_defaults.copy()
query_args.update(kwargs)
url = self.url_template.format(**query_args)
for result in self.interact_yield("POST", url, **callback_settings):
for result in self.interact_yield("POST", url, payload=payload,
**callback_settings):
yield result

def by_mac(self, callback=None, callback_args=None, **kwargs):
Expand Down