-
Notifications
You must be signed in to change notification settings - Fork 23
/
pmatic-manager
executable file
·205 lines (177 loc) · 8.07 KB
/
pmatic-manager
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
#!/usr/bin/env python
# encoding: utf-8
#
# pmatic - Python API for Homematic. Easy to use.
# Copyright (C) 2016 Lars Michelsen <[email protected]>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
"""A daemon for managing pmatic scripts
The pmatic manager is used to manage pmatic scripts on the local system. The
main purpose for this manager is to make the pmatic scripts on the CCU be
manageable using a web GUI. However, it is possible to run the pmatic manager
on any linux system of your choice. This might have the advantage that the
manager and scripts are performing better than running it directly on the CCU.
The tasks of the manager:
* Make it possible to upload and delete scripts
You are completely free to upload, delete or modify the scripts using other
ways, for example via SSH at any time.
* Run the scripts for testing
This is also possible via SSH but more comfortable via your browser using
the pmatic manager.
* Invoke the scripts based on your configuration
The pmatic manager can execute scripts of your choice when specific events
occur like a value changed or based on dates / times.
"""
# Add Python 3.x behaviour to 2.7
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import sys
import traceback
import socket
import argparse
import logging
from logging.handlers import RotatingFileHandler
import pmatic.manager
from pmatic.exceptions import SignalReceived
pmatic.manager.Config.load()
def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument('-a',
dest="address",
default="",
help="Specify another host address than the default (all) to listen on.")
parser.add_argument('-p',
dest="port",
default="9120",
type=int,
help="Specify another TCP port than the default one (9120) to listen on.")
parser.add_argument('-g',
dest="foreground",
action="store_true",
help="Execute the pmatic manager in foreground instead of daemonizing.")
parser.add_argument('-o',
dest="config_path",
help="Specify the location to the pmatic Manager config directory. "
"The default directory is \"/etc/config/addons/pmatic/etc\".",
default=pmatic.manager.Config.config_path)
parser.add_argument('-e',
dest="state_path",
help="Specify the location to the pmatic Manager state directory. "
"The default directory is \"/var/lib/pmatic\".",
default=pmatic.manager.Config.state_path)
parser.add_argument('-r',
dest="script_path",
help="Specify the location to the pmatic script directory. "
"The default directory is \"/etc/config/addons/pmatic/scripts\".",
default=pmatic.manager.Config.script_path)
parser.add_argument('-t',
dest="static_path",
help="Specify the location to the pmatic static file (images, ...) directory. "
"The default directory is \"/etc/config/addons/pmatic/manager_static\".",
default=pmatic.manager.Config.static_path)
parser.add_argument('-l',
dest="log_level",
help="Specify the log level (defaults to INFO). You can choose from the following: "
"CRITICAL, ERROR, WARNING, INFO, DEBUG.",
default=pmatic.manager.Config.log_level or "INFO",
choices=pmatic.log_level_names)
parser.add_argument('-f',
dest="log_file",
help="Configure the location of the file to write log entries to. When "
"the log file reaches a size of ~5MB, it is rotated to LOG_FILE.1, "
"then to LOG_FILE.2 and then deleted. The default log file is "
"\"/var/log/pmatic-manager.log\".",
default=pmatic.manager.Config.log_file)
parser.add_argument('-c',
dest="ccu_address",
help="When the pmatic manager is executed on another host than the CCU, "
"you need to configure the address and credentials to access the "
"CCU. When executed on the CCU, these options are ignored.",
default=pmatic.manager.Config.ccu_address)
parser.add_argument('-u',
dest="ccu_username",
help="When the pmatic manager is executed on another host than the CCU, "
"you need to configure the username to access the CCU. When executed "
"on the CCU, these options are ignored.",
default=pmatic.manager.Config.ccu_credentials[0]
if pmatic.manager.Config.ccu_credentials else "")
parser.add_argument('-s',
dest="ccu_password",
help="When the pmatic manager is executed on another host than the CCU, "
"you need to configure the password of the given username to access "
"the CCU. When executed on the CCU, these options are ignored.",
default=pmatic.manager.Config.ccu_credentials[1]
if pmatic.manager.Config.ccu_credentials else "")
parser.add_argument('-n',
dest="ccu_enabled",
action="store_false",
help="Disable the connection with the CCU. This may be useful when "
"testing/using the pmatic Manager without a CCU.",
default=pmatic.manager.Config.ccu_enabled)
args = parser.parse_args()
return args
args = parse_args()
# If the config_path changed, load the new config. Then load the args
# again because the new config might have changed the arg defaults
if pmatic.manager.Config.config_path != args.config_path:
pmatic.manager.Config.config_path = args.config_path
pmatic.manager.Config.load()
args = parse_args()
# Set log level according to user supplied arguments
pmatic.logging(getattr(pmatic, args.log_level))
# auto rotate on 5MB, rotate files 2 times, then overwrite
logger = logging.getLogger("pmatic")
pmatic.manager.Config.log_file = args.log_file
file_logger = RotatingFileHandler(args.log_file, maxBytes=5*1024*1024, backupCount=2)
file_logger.setLevel(args.log_level)
file_logger.setFormatter(logging.Formatter('%(asctime)s [%(levelname)s] %(message)s'))
# Apply other paths
pmatic.manager.Config.script_path = args.script_path
pmatic.manager.Config.static_path = args.static_path
pmatic.manager.Config.state_path = args.state_path
logger.addHandler(file_logger)
# Set CCU address / credentials when provided by user to override the configuration
pmatic.manager.Config.ccu_enabled = args.ccu_enabled
pmatic.manager.Config.ccu_address = args.ccu_address
if args.ccu_username and args.ccu_password:
pmatic.manager.Config.ccu_credentials = args.ccu_username, args.ccu_password
logger.info("------------------------------------------")
logger.info("Starting up (Version %s)" % pmatic.__version__)
logger.info("------------------------------------------")
try:
manager = pmatic.manager.Manager((args.address, args.port))
except socket.error as e:
if e.errno == 98:
sys.stderr.write("Failed to start. Maybe another pmatic manager process is already "
"running?\n (%s)\n" % e)
sys.exit(1)
else:
raise
if not args.foreground:
manager.daemonize()
manager.init_scheduler()
manager.register_signal_handlers()
manager.init_ccu()
try:
manager.logger.info("Start listening on TCP %d..." % args.port)
manager.serve_forever()
except SignalReceived as e:
manager.logger.info("Killed with signal %d." % e._signum)
sys.exit(1)
except Exception as e:
manager.logger.error("Unhandled exception: %s" % traceback.format_exc())
sys.exit(1)