Module http

This module implements the possibility to write web interfaces for SmartHomeNG plugins.

class modules.http.CherryPyFilter(name='')[source]

Bases: logging.Filter

This class builds a filter to be used in logging.yaml to configure logging

Returning True tells logging to suppress this logentry, whereas False will include the record into further processing and eventual output

filter(record)[source]

Determine if the specified record is to be logged.

Is the specified record to be logged? Returns 0 for no, nonzero for yes. If deemed appropriate, the record may be modified in-place.

class modules.http.Http(sh, port=None, servicesport=None, ip='', threads=8, starturl='', showpluginlist='True', showservicelist='False', showtraceback='False', user='', password='', hashed_password='')[source]

Bases: object

version = '1.6.0'
gtemplates_dir = ''
gstatic_dir = ''
get_user_dict()[source]

Returns the user(s) defined in ../etc/module.yaml (section http) as a dict

The information is a dict containing the hashed_password and a list of groups for each user

Returns:Information of defined users
Return type:dict
validate_password(realm, username, password)[source]

Validate a given user/password combination

Parameters:
  • realm
  • username
  • password
Returns:

validate_service_password(realm, username, password)[source]
get_local_ip_address()[source]

Returns the local ip address under which the webinterface can be reached

Returns:ip address
Return type:str
get_local_hostname()[source]

Returns the local hostname under which the webinterface can be reached

Returns:fully qualified hostname
Return type:str
get_local_port()[source]

Returns the local port under which the webinterface can be reached

Returns:port number
Return type:int
get_local_servicesport()[source]

Returns the local port under which the webservices can be reached

Returns:port number
Return type:int
get_webifs_for_plugin(pluginname)[source]

Returns infos about the registered webinterfaces for a plugin (specified by shortname)

The information is returned as a list of dicts. One listentry for each registered webinterface. The dict for each registered webinterface has the following structure:

webif_dict = {‘mount’: mount,
‘pluginclass’: pluginclass, ‘webifname’: webifname, ‘pluginname’: pluginname, ‘instance’: instance, ‘conf’: conf, ‘description’: description}
Parameters:pluginname (str) – Shortname of the plugin
Returns:Tnfos about the registered webinterfaces
Return type:list of dicts
get_services_for_plugin(pluginname)[source]

Returns infos about the registered webservices for a plugin (specified by shortname)

The information is returned as a list of dicts. One listentry for each registered webservice. The dict for each registered webinterface has the following structure:

service_dict = {‘mount’: mount,
‘pluginclass’: pluginclass, ‘servicename’: servicename, ‘pluginname’: pluginname, ‘instance’: instance, ‘conf’: conf, ‘description’: description}
Parameters:pluginname (str) – Shortname of the plugin
Returns:Tnfos about the registered webservices
Return type:list of dicts
register_webif(app, pluginname, conf, pluginclass='', instance='', description='', webifname='', use_global_basic_auth=True)[source]

Register an application for CherryPy

This method is called by a plugin to register a webinterface

It should be called like this:

self.mod_http.register_webif(WebInterface( … ),
self.get_shortname(), config, self.get_classname(), self.get_instance_name(), description, webifname, use_global_basic_auth)
Parameters:
  • app (object) – Instance of the application object
  • pluginname (str) – Mount point for the application
  • conf (dict) – Cherrypy application configuration dictionary
  • pluginclass (str) – Name of the plugin’s class
  • instance – Instance of the plugin (if multi-instance)
  • description (str) – Description of the functionallity of the webif. If left empty, a generic description will be generated
  • webifname (str) – Name of the webinterface. If left empty, the pluginname is used
  • use_global_basic_auth – if True, global basic_auth settings from the http module are used. If False, registering plugin provides its own basic_auth
Type:

use_global_basic_auth: bool

register_service(app, pluginname, conf, pluginclass='', instance='', description='', servicename='', use_global_basic_auth=True)[source]

Register a service for CherryPy

This method is called by a plugin to register a webservice.

It should be called like this:

self.mod_http.register_service(Webservice( … ),
self.get_shortname(), config, self.get_classname(), self.get_instance_name(), description, servicename, use_global_basic_auth)
Parameters:
  • app (object) – Instance of the service object
  • pluginname (str) – Mount point for the service
  • conf (dict) – Cherrypy application configuration dictionary
  • pluginclass (str) – Name of the plugin’s class
  • instance – Instance of the plugin (if multi-instance)
  • description (str) – Description of the functionallity of the webif. If left empty, a generic description will be generated
  • servicename (str) – Name of the service. I if left empty, the pluginname is used
  • use_global_basic_auth – if True, global basic_auth settings from the http module are used. If False, registering plugin provides its own basic_auth
Type:

use_global_basic_auth: bool

start()[source]

If the module needs to startup threads or uses python modules that create threads, put thread creation code or the module startup code here.

Otherwise don’t enter code here

stop()[source]

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

Otherwise don’t enter code here

class modules.http.ModuleApp(mod, starturl)[source]

Bases: object

The module http implements it’s own webinterface. This WebApp implements the entrypoint for the webinterface of the module ‘http’.

Depenting on the configuration of the ‘http’ module, it redirects to the webinterface of a specified plugin or it redirects to a chooser which allows the start of the differnt webinterfaces of the plugins.

This webinterface is mounted to CherryPy as ‘/’

index()[source]

This method is exposed to CherryPy. It implements the page ‘index.html’

A formatted version of the sample README.md can be found here:

A raw version of the README.md for copy and paste can be found below the metadata file.

The meta data file:

module.yaml
# Metadata for the plugin
module:
    # Global module attributes
    classname: Http
    version: 1.6.0
    sh_minversion: 1.5b
#   sh_maxversion:              # maximum shNG version to use this plugin (leave empty if latest)
    description:
        de: 'Modul zur Implementierung von Backend-Webinterfaces für Plugins'
        en: 'Module for implementing a backend-webinterface for plugins'
        fr: "Module pour l'implémentation d'interfaces web des extensions"

parameters:
    # Definition of parameters to be configured in etc/module.yaml
    user:
        type: str
        default: 'admin'
        description:
            de: Benutzername für den Web Zugriff (Basic Auth), falls ein Passwort definiert ist
            en: username for the web access (basic auth), if a password is defined
            fr: Nom d'utilisateur pour l'accès web (Auth basique) si un mot de passe est défini
    password:
        type: str
        description:
            de: Passwort (im Klartext) für den Web Zugriff
            en: password (unencrypted) for the web access
            fr: Mot de passe (texte clair) pour l'accès web
    hashed_password:
        type: password
        description:
            de: hashed Version des Passworts für den Web Zugriff, ersetzt den Parameter 'password'
            en: hashed version of the password for the web access, supersedes the parameter 'password'
            fr: version haché du mot de passe pour l'interface web, remplace le paramètre 'password'

    service_user:
        type: str
        default: 'serviceuser'
        description:
            de: Benutzername für den Zugriff auf Webservices (Basic Auth), falls ein Passwort definiert ist
            en: username for the access to webervices (basic auth), if a password is defined
            fr: Nom d'utilisateur pour l'accès au service web (Auth basique) si un mot de passe est défini
    service_password:
        type: str
        description:
            de: Passwort (im Klartext) für den Zugriff auf Webservices
            en: password (unencrypted) for the access to webervices
            fr: Mot de passe (texte clair) pour l'accès aux services web
    service_hashed_password:
        type: password
        description:
            de: hashed Version des Passworts für den Zugriff auf Webservices, ersetzt den Parameter 'password'
            en: hashed version of the password for the access to webervices, supersedes the parameter 'password'
            fr: version haché du mot de passe pour l'accès au service web (Auth basique) si un mot de passe est défini
    ip:
        type: ipv4
        description:
            de: IP Adresse auf der das http Modul aktiv sein soll - muss normalerweise nicht angegeben werden
            en: IP adress on which the http module should operate - not needed, has only to be specified for special configurations
            fr: Adresse IP sur laquelle le module http devrait fonctionner - il n'est généralement pas nécessaire de la spécifier
    port:
        type: int
        valid_min: 0
        valid_max: 65535
        default: 8383
        description:
            de: Portnummer für die Webinterfaces
            en: Port number for the access to web interfaces
            fr: Numéro de port pour l'accès à l'interface web
    tls_port:
        type: int
        valid_min: 0
        valid_max: 65535
        default: 8385
        description:
            de: Portnummer für die Webinterfaces bei Nutzung von https
            en: Port number for the access to webervices when using https
            fr: Numéro de port pour l'accès à l'interface web si utilisation de https
    use_tls:
        type: bool
        default: False
        description:
            de: Auf True setzen, um Zugriffe über https zu ermöglichen (Zertifikat muss installiert sein)
            en: Set to true to allow access over https (certificate has to be installed)
            fr: Mettre sur 'true' pour activer les accès par https (certificats doivent être installés)
    tls_cert:
        type: str
        default: shng.cer
        description:
            de: Name der Zertifikatsdatei. Die Datei muss im Verzeichnis ../etc liegen
            en: Name of the certificate file. The file musst be stored in ../etc
            fr: Nom du fichier contanent les certificats. Le fichier doit se trouver dans ../etc
    tls_key:
        type: str
        default: shng.key
        description:
            de: Name der Datei mit dem privaten Schlüssel. Die Datei muss im Verzeichnis ../etc liegen
            en: Name of the private key file. The file musst be stored in ../etc
            fr: Nom du fichier contanent les clés privés. Le fichier doit se trouver dans ../etc

    servicesport:
        type: int
        valid_min: 0
        valid_max: 65535
        default: 8384
        description:
            de: Portnummer für die Webservices
            en: Port number for the access to webervices
            fr: Numéro de port pour les services web

    showpluginlist:
        type: bool
        default: True
        description:
            de: Anzeige einer Liste der Plugins mit Webinterfaces unter der url /plugins
            en: Show a list of plugins with web interfaces under the url /plugins
            fr: Afficher une listes des extensions avec interface web sous l'url /plugins

    showservicelist:
        type: bool
        default: False
        description:
            de: Anzeige einer Liste der Plugins mit Webservices unter der url /services
            en: Show a list of plugins with webservices under the url /services
            fr: Afficher une listes des extensions avec des services web sous l'url /services

    starturl:
        type: str
        default: admin
        description:
            de: Weiterleitungs-Url, wenn SmartHomeNG im Browser nur mit <DNS-Name>:<Portnummer> aufgerufen wird.
            en: Redirection-url, if SmartHomeNG is called in the browser only by <dns-name>:<portnumber>
            fr: Url de redirection si SmartHomeNG est chargé par le navigateur en utilisant seulement <nom-dns>:<numéro de port>.

    threads:
        type: int
        default: 4
        description:
            de: Anzahl Threads, die für jede CherryPy App eingerichtet wird
            en: number of threads setup per CherryPy app
            fr: numéro de tâches prévus par application CherryPy
        valid_list:
          - 2
          - 3
          - 4
          - 5
          - 6
          - 7
          - 8

    showtraceback:
        type: bool
        default: False
        description:
            de: Traceback bei Python Exceptions in der Fehlermeldung im Browser anzeigen. (Sollte in Produktionsinstallationen False sein)
            en: Show traceback of Python exceptions on the error page displayed in the browser. (Should be False in production environment)
            fr: Montrer les traces des exepctions Python sur la page d'erreurs affiché dans le navigateur. (Normallement 'False' dans en environnement de production)
README.md
# Module http

This module allows plugins to implement a web interface. The API is described below. The first plugin to utilize this API is the backend plugin.

> Note: To write a plugin that utilizes this module, you have to be familiar with CherryPy. 


## Requirements

This module is running under SmmartHomeNG versions beyond v1.3. It requires Python >= 3.4 as well as the lib **cherrypy**. You can install the libraries (python modules) with:

```
(sudo apt-get install python-cherrypy)
sudo pip3 install cherrypy
```

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
http:
    module_name: http
#    port: 8383
#    servicesport: 8384
#    showpluginlist: False
    showservicelist: True
#    starturl: backend
#    threads: 8
#    showtraceback: True

```

#### user (optional)

username for the web access. By default username `admin` is used.

#### password (optional)

password for the web access. By default empty. Without a password access is available for everyone.

#### hashed_password (optional)

hashed password for the web access. By default empty. Without a hashed password the parameter password is used.

#### service_user (optional

username for the access to webervices. By default username `serviceuser` is used.

#### service_password (optional)

password for the access to the web services. By default empty. Without a password access is available for everyone.

#### service_hashed_password (optional)

hashed password for access to the web services. By default empty. Without a hashed password the parameter Service_password is used.

#### port (optional)
The port on which the html interface listens. By default port **`8383`** is used.

#### servicesport (optional)
The port on which the html interface listens. By default port **`8384`** is used.

#### showpluginlist
If set to `False` no list of pluins with web interface is shown under `smarthomeNG.local:8383/plugins`. By default, **showpluginlist** is **True**.

#### showservicelist
If set to `True` a list of webservices is shown under `smarthomeNG.local:8384/services`. By default, ** showservicelist** is **False**.

#### starturl (optional)
The name of the plugin that is started when calling url `smarthomeNG.local:8383` without further detailing that url. If you want to startup the **backend** plugin for example: You set `starturl: backend`. That results in a redirect which redirects `smarthomeNG.local:8383` to `smarthomeNG.local:8383/backend`. 

if `starturl` is not specified or point to an url that does not exist, a redirect to `smarthomeNG.local:8383/plugins` will take place (if ** showpluginlist** is **True**). It points to a page that lists all plugins that have registered a html interface and allows you to start those interfaces.

> Note: If you have redirected to a specific plugin, you can always get to the page with the list of all plugins that have registered a html interface, by entering the url `smarthomeNG.local:8383/plugins`.

####  threads (optional)
Number of worker threads to start by cherrypy (default 8, which may be too much for slow CPUs)

#### showtraceback
If set to **True, error-pages (except for error 404) will show the Python traceback for that error.


## API of module http

### Test if module http is loaded

`http` 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_http = self._sh.get_module('http')
except:
    self.mod_http = None
    
if self.mod_http == None:
    # Do what is necessary if you can't start a web interface
    # for your plugin. For example:
    self.logger.error('{}: Module ''http'' not loaded - Abort loading of plugin {0}'.format(self.classname))
    return
```

### Registering a web application/interface

For registering a web interface (or a web application in CherryPy terminology) you first have to define an application configuration for cherrypy.

> Note: Be careful not to include a CherryPy ``global`` configuration.

An application configuration for CherryPy can look like this;

```
app_config = {
    '/': {
        'tools.staticdir.root': current_dir,
        'tools.auth_basic.on': self._basic_auth,
        'tools.auth_basic.realm': 'earth',
        'tools.auth_basic.checkpassword': self.validate_password,
    },
    '/static': {
        'tools.staticdir.on': True,
        'tools.staticdir.dir': os.path.join(current_dir, 'static')
    }
}
```

> Note: The `tools.auth_basic`entries in this example are for implementing a basic logon security. If you don`t want/need login security, delete those enties.

For registering a web application/interface you have to call the `register_app` of module `http`:

```
register_app(app_object, 
             appname, 
             app_config, 
             pluginclass, instance,
             description)
```

For example:

```
appname = 'backend'    # Name of the plugin
pluginclass = self.__class__.__name__
instance = self.get_instance_name()

self.mod_http.register_app(Backend(self, self.updates_allowed, language, self.developer_mode, self.pypi_timeout), 
                          appname, 
                          app_config, 
                          pluginclass, instance,
                          description='Administration interface for SmartHomeNG')
```

## Implementing a web interface for you plugin

For details about implementing a web interface (CherryPy application) for your plugin, refer to the CherryPy documentation.

The documentation will tell you how to expose parts of your python code to be availabe through CheryPy`s http-server.

### Methods for implementing a web interface

#### get_local_ip_address()
Returns the ip address under which the web interface is listening.

#### get_local_hostname()
Returns the hostname (with domain) under which the web interface is listening.

#### get_local_port()
Returns the port under which the implemented web interfaces can be reached.

#### get_local_servicesport( ... )
Returns the port under which the implemented webservices can be reached.

#### register_app()

##### Parameters
- **app**	- Instance of the CherryPy App
- **pluginname**  - Standard would be: Shortname of the plugin (name of the plugin's directory)
- **conf**  - dict with CherryPy App-Config
- **pluginclass**  - Class of the plugin
- **instance**   - Optional: Instance of the plugin (if multi-instance)
- **description**  - Optional: Description to be shown on page with plugin-list

#### register_service( ... )

##### Parameters
- **service**	- Instance of the CherryPy App
- **servicename**  - Standard would be: Shortname of the plugin (name of the plugin's directory)
- **conf**  - dict with CherryPy App-Config
- **pluginclass**  - Class of the plugin
- **instance**   - Optional: Instance of the plugin (if multi-instance)
- **description**  - Optional: Description to be shown on page with services-list