4 from typing import List, Optional, Set
10 import smart_home.device as device
11 import smart_home.lights as lights
12 import smart_home.outlets as outlets
14 parser = config.add_commandline_args(
15 f"Smart Home Config ({__file__})",
16 "Args related to the smart home config."
19 '--smart_home_config_file_location',
20 default='/home/scott/bin/network_mac_addresses.txt',
22 help='The location of network_mac_addresses.txt',
23 type=argparse_utils.valid_filename,
27 class SmartHomeConfig(object):
30 config_file: Optional[str] = None,
31 filters: List[str] = ['smart'],
33 if config_file is None:
34 config_file = config.config[
35 'smart_home_config_file_location'
37 assert file_utils.does_file_exist(config_file)
38 with open(config_file, "r") as f:
39 contents = f.readlines()
41 self._macs_by_name = {}
42 self._keywords_by_name = {}
43 self._keywords_by_mac = {}
44 self._names_by_mac = {}
45 self._corpus = logical_search.Corpus()
48 line = line.rstrip("\n")
49 line = re.sub(r"#.*$", r"", line)
53 (mac, name, keywords) = line.split(",")
56 keywords = keywords.strip()
58 if filters is not None:
63 self._macs_by_name[name] = mac
64 self._keywords_by_name[name] = keywords
65 self._keywords_by_mac[mac] = keywords
66 self._names_by_mac[mac] = name
67 self.index_device(name, keywords, mac)
69 def index_device(self, name: str, keywords: str, mac: str) -> None:
70 properties = [("name", name)]
72 for kw in keywords.split():
74 key, value = kw.split(":")
75 properties.append((key, value))
78 device = logical_search.Document(
81 properties=properties,
84 self._corpus.add_doc(device)
86 def __repr__(self) -> str:
87 s = "Known devices:\n"
88 for name, keywords in self._keywords_by_name.items():
89 mac = self._macs_by_name[name]
90 s += f" {name} ({mac}) => {keywords}\n"
93 def get_keywords_by_name(self, name: str) -> Optional[device.Device]:
94 return self._keywords_by_name.get(name, None)
96 def get_macs_by_name(self, name: str) -> Set[str]:
98 for (mac, lname) in self._names_by_mac.items():
103 def get_macs_by_keyword(self, keyword: str) -> Set[str]:
105 for (mac, keywords) in self._keywords_by_mac.items():
106 if keyword in keywords:
110 def get_device_by_name(self, name: str) -> Optional[device.Device]:
111 if name in self._macs_by_name:
112 return self.get_device_by_mac(self._macs_by_name[name])
115 def get_all_devices(self) -> List[device.Device]:
117 for (mac, kws) in self._keywords_by_mac.items():
119 device = self.get_device_by_mac(mac)
120 if device is not None:
121 retval.append(device)
124 def get_device_by_mac(self, mac: str) -> Optional[device.Device]:
125 if mac in self._keywords_by_mac:
126 name = self._names_by_mac[mac]
127 kws = self._keywords_by_mac[mac]
128 if 'light' in kws.lower():
129 if 'tplink' in kws.lower():
130 return lights.TPLinkLight(name, mac, kws)
131 elif 'tuya' in kws.lower():
132 return lights.TuyaLight(name, mac, kws)
133 elif 'goog' in kws.lower():
134 return lights.GoogleLight(name, mac, kws)
136 raise Exception(f'Unknown light device: {name}, {mac}, {kws}')
137 elif 'outlet' in kws.lower():
138 if 'tplink' in kws.lower():
139 if 'children' in kws.lower():
140 return outlets.TPLinkOutletWithChildren(name, mac, kws)
142 return outlets.TPLinkOutlet(name, mac, kws)
143 elif 'goog' in kws.lower():
144 return outlets.GoogleOutlet(name, mac, kws)
146 raise Exception(f'Unknown outlet device: {name}, {mac}, {kws}')
148 return device.Device(name, mac, kws)
151 def query(self, query: str) -> List[device.Device]:
152 """Evaluates a lighting query expression formed of keywords to search
153 for, logical operators (and, or, not), and parenthesis.
154 Returns a list of matching lights.
157 results = self._corpus.query(query)
158 if results is not None:
161 device = self.get_device_by_mac(mac)
162 if device is not None:
163 retval.append(device)