Modul admin

Dieses Modul implementiert ein graphisches Administrations-Interface für SmartHomeNG. Das ermöglicht die vollständige Konfiguration von SmartHomeNG.

Das Modul implementiert eine Reihe von REST Interfaces, die vom „Web Interface“, einer Angular Applikation genutzt werden um die Daten im Browser anzuzeigen bzw. zu manipulieren. Das Web Interface wird über das http Modul bereit gestellt, wie es auch bei den Web Interfaces der Plugins der Fall ist.

API des Moduls

Im folgenden werden die einzelnen APIs des Moduls beschrieben.

API des Moduls admin.rest

RESTResource ist die Grundlegende Klasse um REST Interfaces über das im http Modul verwendete CherryPy zur Verfügung zu stellen.

class modules.admin.rest.RESTResource[Quellcode]

REST Resource

cherrypy controller mixin to make it easy to build REST applications.

handles nested resources and method-based dispatching.

here’s a rough sample of what a controller would look like using this:

cherrypy.root = MainController() cherrypy.root.user = UserController()

class PostController(RESTResource):
def index(self,post):

return post.as_html()

index.expose_resource = True

def delete(self,post):

post.destroySelf() return „ok“

delete.expose_resource = True

def update(self,post,title=““,body=““):

post.title = title post.body = body return „ok“

update.expose_resource = True

def add(self, post, title=““, body=““)

post.title = title post.body = body return „ok“

update.expose_resource = True

def REST_instantiate(self, slug):
try:

return Post.select(Post.q.slug == slug, Post.q.userID = self.parent.id)[0]

except:

return None

def REST_create(self, slug):

return Post(slug=slug,user=self.parent)

class UserController(RESTResource):

REST_children = {‚posts‘ : PostController()}

def index(self,user):

return user.as_html()

index.expose_resource = True

def delete(self,user):

user.destroySelf() return „ok“

delete.expose_resource = True

def update(self,user,fullname=““,email=““):

user.fullname = fullname user.email = email return „ok“

update.expose_resource = True

def add(self, user, fullname=““, email=““):

user.fullname = fullname user.email = email return „ok“

add.expose_resource = True

def extra_action(self,user):

# do something else

extra_action.expose_resource = True

def REST_instantiate(self, username):
try:

return User.byUsername(username)

except:

return None

def REST_create(self, username):

return User(username=username)

then, the site would have urls like:

/user/bob /user/bob/posts/my-first-post /user/bob/posts/my-second-post

which represent REST resources. calling ‚GET /usr/bob‘ would call the index() method on UserController for the user bob. ‚PUT /usr/joe‘ would create a new user with username ‚joe‘. ‚DELETE /usr/joe‘ would delete that user. ‚GET /usr/bob/posts/my-first-post‘ would call index() on the Post Controller with the post with the slug ‚my-first-post‘ that is owned by bob.


API des Moduls api_plugins

class modules.admin.api_plugins.PluginsController(module)[Quellcode]

Bases: RESTResource

read(id=None)[Quellcode]

Handle GET requests for threads API

return an object with type info about all installed plugins

class modules.admin.api_plugins.PluginsInstalledController(module)[Quellcode]

Bases: RESTResource

read(id=None)[Quellcode]

return an object with data about all installed plugins

class modules.admin.api_plugins.PluginsConfigController(module)[Quellcode]

Bases: RESTResource

read(id=None)[Quellcode]

return an object with data about all configured plugins

class modules.admin.api_plugins.PluginsInfoController(module, shng_url_root)[Quellcode]

Bases: RESTResource

blog_urls = {}
stop()[Quellcode]

If the Controller has started threads or uses python modules that created threads, put cleanup code here.

read(id=None)[Quellcode]

return a list of all configured plugin instances

class modules.admin.api_plugins.PluginsAPIController(module)[Quellcode]

Bases: RESTResource

read(id=None)[Quellcode]

return a list of all configured plugin instances

class modules.admin.api_plugins.PluginsLogicParametersController(module)[Quellcode]

Bases: RESTResource

read(id=None)[Quellcode]

return an object with data about the logic parameters of all configured plugins


API des Moduls api_plugin

class modules.admin.api_plugin.PluginController(module, jwt_secret=False)[Quellcode]

Bases: RESTResource

get_body()[Quellcode]

Get content body of received request header

Rückgabe:

test_for_old_config(config_filename)[Quellcode]
get_config_filename()[Quellcode]
read(id=None)[Quellcode]

return an object with type info about all installed plugins

add(id=None)[Quellcode]
handle_plugin_action(id, action)[Quellcode]
handle_plugin_lifecycle(id, action)[Quellcode]
update(id='', action='')[Quellcode]
delete(id=None)[Quellcode]

API des Moduls (WebApi)

class modules.admin.WebApi(webif_dir, module, shng_url_root, url_root)[Quellcode]

Bases: RESTResource

Parameter:
  • webif_dir (str) – Directory where the files of the web interface (shngadmin) are stored

  • module (object) – Instance of the webif object

  • shng_url_root (str) –

  • url_root (str) –


README

README
# Module admin (README)

This module implements the administration interface for SmartHomeNG.


## Requirements

This module is running under SmmartHomeNG versions beyond develop version v1.5d. It requires Python >= 3.4 as well as ... . You can install the libraries (python modules) with:

```
(sudo apt-get install ...)
sudo pip3 install ...
```

And please pay attention that the lib(s) are installed for Python3 and not an older Python 2.7 that is probably installed on your system. Be carefull to use `pip3` and nor `pip`.

> Note: This module needs the module handling in SmartHomeNG to be activated. Make sure, that `use_modules`in `etc/smarthome.yaml` is **not** set to False!


## Configuration

### etc/module.yaml


```yaml
# etc/module.yaml
admin:
    module_name: admin
```


## API des Moduls admin

This module exposes a JWT-authenticated REST API under `/api/`, consumed by the shngadmin
frontend (a separate repository). The full endpoint-by-endpoint reference — every route,
method, query/body params, and auth requirement — lives in
[openapi.yaml](openapi.yaml); this section is a grouped overview, reconstructed 2026-07-20
from the actual handler code in `api_*.py` and cross-checked against every call site in
shngadmin. Where this file and `openapi.yaml` disagree, trust `openapi.yaml` — it's the
more detailed source.

**`openapi.yaml` is generated, not hand-edited** — same convention as `requirements/*.txt`
(see `lib/shpypi.py`). Its actual source of truth is the `ApiDoc`/`ApiParam` metadata
attached directly to each route's handler method in `api_*.py` (see `rest.py` for the
type definitions) — e.g. `read.api_doc = [ApiDoc(...)]` sitting right next to that same
method's existing `read.expose_resource = True` line. This replaced a hand-maintained
`api.raml` that went stale because nothing forced it to change alongside the code; putting
the metadata in the same few lines as the route it documents is the actual fix, not just a
different file format. After changing a route (or its `ApiDoc`), regenerate with:

```
python tools/build_openapi.py
```

Note: `/admin/` (without `/api`) is *not* part of this API — it's the suburl the built
Angular frontend's static files are served from. An older, unauthenticated set of
JSON/HTML endpoints used to live there before shngadmin migrated to this REST API; they
have since been removed (see the `WebInterface` class docstring in `__init__.py`).

Unless noted otherwise, every endpoint below requires the `Authorization: Bearer <jwt>`
header.

### AUTH API — login and token renewal

- `POST /api/authenticate/user` — log in with SHA-512-hashed credentials, get a JWT. *No auth required.*
- `PUT /api/authenticate/renew` — renew the current JWT before it expires.

### CONFIG API — read/write etc/smarthome.yaml and etc/module.yaml sections

- `GET /api/config/` — all config sections (common, http, websocket, admin, mqtt) in one call.
- `GET /api/config/{common|http|websocket|admin|mqtt}` — a single section (backend only; shngadmin always fetches everything via the bare `GET /config/`).
- `PUT /api/config/core` — save config. Despite the path, this writes *all* sections together in one call, regardless of which id (`core`/`common`/`http`/`admin`/`mqtt`) is used — shngadmin always uses `core`.
- `GET /api/config/check_config_etc/` — dry-run check whether `etc/` needs/can be migrated.
- `PUT /api/config/enable_config_etc/` — (re-)enable the `etc/` config directory.

### ITEMS API — item tree, item CRUD, struct templates

- `GET /api/items/list/` — flat item list.
- `GET /api/items/tree` — item tree structure.
- `GET /api/items/attributes` — core item-attribute catalog.
- `GET /api/items/structs/` — item struct templates (`etc/struct.yaml`).
- `GET /api/items/{itemPath}` — full detail for one item.
- `PUT /api/items/{itemPath}` — live-set an item's value.
- `POST /api/items/{itemPath}` — create a new item.
- `PATCH /api/items/{itemPath}` — replace an item's config (full replace, not a partial patch despite the verb).
- `DELETE /api/items/{itemPath}?persist=&recursive=` — delete an item.
- `POST /api/items/{itemPath}/rename` — rename or move an item (same endpoint for both).
- `POST /api/items/{itemPath}/copy` — copy an item, subtree included by default.
- `GET /api/items/{itemPath}/references` — what references this item (pre-delete check).
- `POST /api/items/{itemPath}/remove_references` — strip other items'/plugins' references to this item.

### PLUGINS API — read-only plugin discovery/info

- `GET /api/plugins/` — **deactivated 2026-07-20** (now 404s). Was an installed-plugin-name → type map, a strict subset of `GET /api/plugins/installed/`, which shngadmin already fetches. Being test-run as removed — see `api_plugins.py`'s `PluginsController.read`.
- `GET /api/plugins/installed/` — full metadata for every plugin in the `plugins/` directory.
- `GET /api/plugins/config/` — configuration of all currently configured plugins.
- `GET /api/plugins/info/` — plugin metadata for the plugin-list page.
- `GET /api/plugins/logicparams/` — plugin-contributed logic-parameter metadata.
- `GET /api/plugins/api/` — plugin API metadata.

### PLUGIN API — single-plugin config CRUD and lifecycle control

- `PUT /api/plugin/{pluginSection}/` — update one plugin's config section.
- `POST /api/plugin/{pluginSection}/` — add a new plugin config section.
- `DELETE /api/plugin/{pluginSection}/` — delete a plugin config section.
- `PUT /api/plugin/{pluginConfigName}?action={start|stop|load|unload|reload}[&filename=]` — lifecycle/state control.

### LOGICS API — logic list, detail, lifecycle, groups, parameters

- `GET /api/logics/[?infotype=groups]` — flat list, or the logic-group tree.
- `GET /api/logics/{logicName}[?infotype=status]` — one logic's detail, or its runtime status.
- `PUT /api/logics/{logicName}?action={trigger|enable|disable|load|unload|reload|delete|create|rename}[&filename=][&newfilename=]` — lifecycle/state actions and rename. (`delete_with_code` is a backend-only variant, also deletes the logic's `.py` file — not currently called by shngadmin.)
- `PUT /api/logics/{logicName}?action=saveparameters` — save the logic's parameter section.
- `PUT /api/logics/{groupName}?action=savegroup` — save a logic group.
- `PUT /api/logics/{groupName}?action=deletegroup` — delete a logic group.

### SCENES API — scene list and reload

- `GET /api/scenes/` — list of configured scenes.
- `PUT /api/scenes/reload/{name}` — reload one scene, or `all` for every scene.

### SCHEDULERS API — scheduler list

- `GET /api/schedulers/` — list of configured schedulers.

### THREADS API — running threads

- `GET /api/threads/` — list of running threads.

### FUNCTIONS API — registered functions

- `GET /api/functions/` — list of registered functions.
- `PUT /api/functions/reload/{name}` — reload one function, or `all` for every function.

### SERVICES API — config-text validation/conversion, cache maintenance

- `PUT /api/services/evalcheck/` — validate a Python `eval:`-style expression.
- `PUT /api/services/yamlcheck/` — validate raw YAML text.
- `PUT /api/services/yamlconvert/` — convert arbitrary config text to YAML.
- `GET /api/services/cachecheck/` — list orphaned cache files.
- `PUT /api/services/cachefile_delete?filename=` — delete one cache file.

### FILES API — raw read/write access to etc/ config files

`filetype` is a closed set: `structs`, `items`, `scenes`, `functions`, `logics` (list files
of that type, or read/write one with `?filename=`), plus the special cases `logging`
(`etc/logging.yaml`) and `backup` (GET only, returns a zip of the whole `etc/` dir).

- `GET /api/files/{filetype}/[?filename=]` — file list, or one file's raw text.
- `POST /api/files/{filetype}/?filename=` — create a new file (409 if it already exists).
- `PUT /api/files/{filetype}/[?filename=]` — save/overwrite a file. For `logging`, the response includes `config_reloaded`/`config_restored` flags — bad YAML is auto-rolled back server-side.
- `DELETE /api/files/{filetype}/[?filename=]` — delete a file.
- `GET /api/files/backup/` — download a config backup.
- `PUT /api/files/restore?filename=` — restore `etc/` from a backup (backend only — no current shngadmin caller found).

### LOGGERS API — logger list, level/handler control

- `GET /api/loggers/` — all loggers with current levels.
- `PUT /api/loggers/{logger}?level=` — set a logger's level.
- `PUT /api/loggers/{logger}?handlers=` — set a logger's handler list.
- `POST /api/loggers/{logger}/` — add a new logger.
- `DELETE /api/loggers/{logger}/` — delete a logger.

### LOGS API — log file list and chunked reading

- `GET /api/logs/` — list of available log files.
- `GET /api/logs/{filename}?chunk=` — one chunk of a log file (`chunk=1` first, `chunk=0` is the server convention for "last chunk").

### SERVER API — bootstrap info, status, restart, PyPI check

- `GET /api/server/` — basic info (`default_language`, `client_ip`, `login_required`, `websocket_host`, `websocket_port`). *No auth required* even though it's flagged `authentication_needed` in code — exempted via `public_root` for exactly this bare path. Also (ab)used as a lightweight reachability heartbeat by shngadmin.
- `GET /api/server/info` — extended info: timezone, core/plugins git branch, `developer_mode`, `dark_mode` default, `resource_graph_period`, `restart_stops_only`, and more.
- `GET /api/server/status/` — shng core running/stopped status.
- `PUT /api/server/restart/` — restart shng core.
- `GET /api/server/pypi` — PyPI package/version check data.

### SYSTEM API — OS/process-level stats

- `GET /api/system/info` — CPU/memory/disk stats for the System Overview page.

### WebSocket API — live item values and time-series charts

Separate from the REST API above: `ws(s)://{host}:{ws_port}/adm`, handled by the
`websocket` module's admin protocol (`modules/websocket/admin.py`), documented in
[websocket_admingui_requests.rst](websocket_admingui_requests.rst). Used for live item
value monitoring and the System Overview page's resource-usage graphs. No JWT is used on
this channel.

##


### Test if module admin is loaded

`admin` is a loadlable module. Therefore there is no guarantiee that it is present in every system. Before you can use this module, you have to make sure ist is loaded. You can do it by calling a method of the main smarthome object. Do it like this:

```
self.classname = self.__class__.__name__

try:
    self.mod_admin = self._sh.get_module('admin')
except:
    self.mod_admin = None
    
if self.mod_admin == None:
    # Do what is necessary if you can't use the admin interface
    # for your plugin. For example:
    self.logger.error('{}: Module ''admin'' not loaded - Abort loading of plugin {0}'.format(self.classname))
    return
```

Metadaten

Auskommentierte Parameter in den Metadaten sind noch nicht implementiert. Die Implementierung dieser Parameter wird im Rahmen der Weiterentwicklung von SmartHomeNG erfolgen:

module.yaml
# Metadata for the plugin
module:
    # Global module attributes
    classname: Admin
    version: 1.9.0
    sh_minversion: 1.12.0
#    sh_maxversion: 1.3             # maximum shNG version to use this plugin (leave empty if latest)
    description:
        de: 'Dieses Modul implementiert das Administrationsinterface von SmartHomeNG'
        en: 'This module implements the administration interface for SmartHomeNG'
        fr: "Ce module implémente l'interface d'administration pour SmartHomeNG"

parameters:
    login_expiration:
        type: num
        default: 48
        description:
            de: 'Ablaufdauer des Logins in Stunden (ab Loginzeitpunkt)'
            en: 'Expiration time of the login in hours (counting from time of login)'
            fr: "Temps d'expiration de l'identification en heures (à partir du moment de l'identification)"

    login_autorenew:
        type: bool
        gui_type: yes_no
        default: True
        description:
            de: 'Bestehendes Login-Token automatisch verlängern'
            en: 'Automatically extend an existing login-token'
#            fr: ""

    itemtree_fullpath:
        type: bool
        gui_type: yes_no
        default: True
        description:
            de: 'Im Item-Tree die Items mit vollem Pfad-Namen anzeigen'
            en: 'Display items with full path in item tree'
            fr: "Afficher le chemin complèt des objets dans l'arborescence"

    itemtree_searchstart:
        type: num
        default: 2
        description:
            de: 'Minimale Anzahl eingegebener Zeichen ab denen die Suche im Item-Tree beginnt'
            en: 'Minimum number of entered characters to start searching the item tree'
            fr: "Nombre minimum de caractères entrés pour démarrer la recherche dans l'arborescence"

    websocket_host:
        type: ip
        default: 'None*'
        description:
            de: 'VERALTET: IP Adresse für den Websocket Zugriff - bitte im Websocket-Modul konfigurieren'
            en: 'DEPRECATED: ip address for websocket access - configure in the websocket module instead'
            fr: "OBSOLÈTE: Adresse IP pour l'accès websocket - à configurer dans le module websocket"

    websocket_port:
        type: int
        valid_min: 0
        valid_max: 65535
        default: 2424
        description:
            de: 'VERALTET: Port für den Websocket Zugriff - bitte im Websocket-Modul konfigurieren'
            en: 'DEPRECATED: port for websocket access - configure in the websocket module instead'
            fr: "OBSOLÈTE: Port pour l'accès websocket - à configurer dans le module websocket"

    log_chunksize:
        type: int
        valid_min: 25
        valid_max: 5000
        default: 1000
        description:
            de: 'Größe der gelesenen Blöcke bei der Anzeige großer Logdateien'
            en: 'Size of blocks (chunks) read when displaying large logfiles'
            fr: "Taille des morceaux lus lors de l'affichage de fichiers journaux volumineux"

    developer_mode:
        type: bool
        default: False
        gui_type: on_off

        description:
            de: 'Entwickler Modus aktivieren (Ist für das Core Entwickler Team gedacht)'
            en: 'Activate developer mode'

    rest_dispatch_force_exception:
        type: bool
        gui_type: yes_no
        default: False
        description:
            de: 'Sollen WARNINGs aus REST_dispatch_execute als EXECPTION geloggt werden?'
            en: 'Activate developer mode'

    click_dropdown_header:
        type: bool
        gui_type: yes_no
        default: False
        description:
            de: 'Click auf Kopfeintrag von Dropdown Menüs erlauben'
            en: 'Allow click on header of dropdown menus'

    dark_mode:
        type: bool
        gui_type: on_off
        default: False
        description:
            de: 'Dark Mode als Standard für das Administrations-Interface aktivieren (kann pro Browser über den
                 Schalter in der Navigationsleiste übersteuert werden)'
            en: 'Activate dark mode as the default for the admin interface (can be overridden per browser via the
                 toggle in the navigation bar)'

    resource_graph_period:
        type: str
        default: '24h'
        description:
            de: 'Zeitraum, den die Ressourcen-Graphen (Systemeigenschaften) initial anzeigen, z.B. "1h", "6h", "24h".
                 Ein kürzerer Zeitraum lässt die Graphen häufiger live aktualisieren, da das Aktualisierungsintervall
                 an die Anzahl der dargestellten Messpunkte gekoppelt ist.'
            en: 'Time span initially shown by the resource graphs (System properties), e.g. "1h", "6h", "24h".
                 A shorter span makes the graphs update live more often, since the update interval is tied to the
                 number of plotted data points.'

    start_page:
        type: str
        default: 'dashboard'
        valid_list: ['dashboard', 'system', 'items', 'logics', 'plugins', 'scenes', 'schedulers', 'services', 'logs']
        description:
            de: 'Seite, die nach dem Login im Administrations-Interface angezeigt wird'
            en: 'Page shown after login in the admin interface'
            fr: "Page affichée après la connexion dans l'interface d'administration"