-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathconfig.py
121 lines (95 loc) · 3.61 KB
/
config.py
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
"""Manages configuration file."""
import collections
import configparser
import logging
from typing import Dict, List, Optional
Device = collections.namedtuple(
'Device', ['name', 'serial', 'credentials', 'product_type'])
DysonLinkCredentials = collections.namedtuple(
'DysonLinkCredentials', ['username', 'password', 'country'])
logger = logging.getLogger(__name__)
class Config:
"""Reads the configuration file and provides handy accessors.
Args:
filename: path (absolute or relative) to the config file (ini format).
"""
def __init__(self, filename: str):
self._filename = filename
self._config = self.load(filename)
@classmethod
def load(cls, filename: str):
"""Reads configuration file.
Returns DysonLinkCredentials or None on error, and a dict of
configured device serial numbers mapping to IP addresses
"""
config = configparser.ConfigParser()
logger.info('Reading "%s"', filename)
try:
config.read(filename)
except configparser.Error as ex:
logger.critical('Could not read "%s": %s', filename, ex)
raise ex
return config
@property
def dyson_credentials(self) -> Optional[DysonLinkCredentials]:
"""Cloud Dyson API credentials.
In the config, this looks like:
[Dyson Link]
username = user
password = pass
country = XX
Returns:
DysonLinkCredentials.
"""
try:
username = self._config['Dyson Link']['username']
password = self._config['Dyson Link']['password']
country = self._config['Dyson Link']['country']
return DysonLinkCredentials(username, password, country)
except KeyError as ex:
logger.warning(
'Required key missing in "%s": %s', self._filename, ex)
return None
@property
def hosts(self) -> Dict[str, str]:
"""Loads the Hosts section, which is a serial -> IP address override.
This is useful if you don't want to discover devices using zeroconf. The Hosts section
looks like this:
[Hosts]
AB1-UK-AAA0111A = 192.168.1.2
"""
try:
hosts = self._config.items('Hosts')
except configparser.NoSectionError:
hosts = []
logger.debug(
'No "Hosts" section found in config file, no manual IP overrides are available')
# Convert the hosts tuple (('serial0', 'ip0'), ('serial1', 'ip1'))
# into a dict {'SERIAL0': 'ip0', 'SERIAL1': 'ip1'}, making sure that
# the serial keys are upper case (configparser downcases everything)
return {h[0].upper(): h[1] for h in hosts}
@property
def devices(self) -> List[Device]:
"""Consumes all sections looking for device entries.
A device looks a bit like this:
[AB1-UK-AAA0111A]
name = Living room
active = true
localcredentials = 12345==
serial = AB1-UK-AAA0111A
... (and a few other fields)
Returns:
A list of Device objects.
"""
sections = self._config.sections()
ret = []
for sect in sections:
if not self._config.has_option(sect, 'LocalCredentials'):
# This is probably not a device entry, so ignore it.
continue
ret.append(Device(
self._config[sect]['Name'],
self._config[sect]['Serial'],
self._config[sect]['LocalCredentials'],
self._config[sect]['ProductType']))
return ret