Module http

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

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.4.6'
gtemplates_dir = ''
gstatic_dir = ''
validate_password(realm, username, password)[source]
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 plugin attributes
    classname: Http
    version: 1.4.6
    sh_minversion: 1.4
#   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'

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
    password:
        type: str
        description:
            de: Passwort (im Klartext) für den Web Zugriff
            en: password (unencrypted) for the web access
    hashed_password:
        type: str
        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'

    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
    service_password:
        type: str
        description:
            de: Passwort (im Klartext) für den Zugriff auf Webservices
            en: password (unencrypted) for the access to webervices
    service_hashed_password:
        type: str
        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'
    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 deeded, has only to be specified for special configurations
    port:
        type: int
        default: 8383
        description:
            de: Portnummer für die Webinterfaces
            en: Port number for the access to webervices
    servicesport:
        type: int
        default: 8384
    showpluginlist:
        type: bool
        default: True
    showservicelist:
        type: bool
        default: False
    starturl:
        type: str
        default: 
    threads:
        type: int
        default: 8
        valid_list:
          - 2
          - 3
          - 4
          - 5
          - 6
          - 7
          - 8
    showtraceback:
        type: bool
        default: False
        
#    test1:
#        Test auf einen ungültigen Default Wert (-1 bei Datentyp positive Integer): es wird der default für pint (0) verwendet
#        type: pint
#        default: -1

#    test2:
#        Test auf einen ungültigen Default Wert (8 ist nicht Element der Liste): es wird der 1. Listeneintrag verwendet
#        type: int
#        default: 8
#        valid_list:
#          - 4
#          - 2
#          - 3
#          - 5
#          - 6
        
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