def scan(self) -> bool: """SKSCANでスマートメーターを探し、接続パラメータを取得. Returns: 成功したときはパラメータをselfに設定し、Trueを返す。 """ params: typ.Dict[str, str] = self.sk.scan_pan() if len(params) == 0: print("スマートメーターが見つかりませんでした。") return False channel: str = params["Channel"] channel_page: str = params.get("Channel Page", "0") pan_id: str = params["Pan ID"] addr: str = params["Addr"] lqi: str = params.get("LQI", "0") pair_id: str = params.get("PairID", "") store: db_store.DBStore = db_store.DBStore(self.db_url) store.scan_log(int(channel, 16), int(channel_page, 16), int(pan_id, 16), addr, int(lqi, 16), pair_id) del store ipv6addr: str = self.sk.skll64(addr) if not re.match(r"([0-9A-F]{4}:){7}[0-9A-F]{4}", ipv6addr): print(f"スマートメーターのIPv6アドレスの取得に失敗しました。 [{ipv6addr}]") return False # 接続パラメータをselfに保存する。 self.channel = channel self.pan_id = pan_id self.ipv6addr = ipv6addr return True
def get_prop(self) -> bool: """property値読み出し. Returns: 成功したときTrue """ epc_list: typ.List[int] = [ echonet.EPC_係数, echonet.EPC_積算電力量計測値, echonet.EPC_積算電力量単位, echonet.EPC_瞬時電力計測値, echonet.EPC_瞬時電流計測値, ] props: typ.Optional[typ.List] = self.sk.get_prop( self.ipv6addr, epc_list, ) if props is None: return False propdict: typ.Dict[int, bytes] = {} for p in props: propdict[p.epc] = p.edt 係数: typ.Optional[int] = None 積算電力量: typ.Optional[int] = None 電力量単位: typ.Optional[int] = None 瞬時電力: typ.Optional[int] = None 瞬時電流_R: typ.Optional[int] = None 瞬時電流_T: typ.Optional[int] = None if echonet.EPC_係数 in propdict: 係数 = struct.unpack_from("!L", propdict[echonet.EPC_係数])[0] if echonet.EPC_積算電力量計測値 in propdict: 積算電力量 = struct.unpack_from("!L", propdict[echonet.EPC_積算電力量計測値])[0] if echonet.EPC_積算電力量単位 in propdict: 電力量単位 = struct.unpack_from("B", propdict[echonet.EPC_積算電力量単位])[0] if echonet.EPC_瞬時電力計測値 in propdict: 瞬時電力 = struct.unpack_from("!l", propdict[echonet.EPC_瞬時電力計測値])[0] if echonet.EPC_瞬時電流計測値 in propdict: 瞬時電流_R = struct.unpack_from("!h", propdict[echonet.EPC_瞬時電流計測値])[0] 瞬時電流_T = struct.unpack_from("!h", propdict[echonet.EPC_瞬時電流計測値], 2)[0] store: db_store.DBStore = db_store.DBStore(self.db_url) store.power_log(係数, 積算電力量, 電力量単位, 瞬時電力, 瞬時電流_R, 瞬時電流_T) del store self.add_zabbix("coefficient", 係数) self.add_zabbix("energy", 積算電力量) self.add_zabbix("energy_unit", 電力量単位) self.add_zabbix("power", 瞬時電力) self.add_zabbix("current_R", 瞬時電流_R) self.add_zabbix("current_T", 瞬時電流_T) return True
def log_bme280(self) -> typ.Tuple: """BME280の情報を記録する. Returns: 気圧, 気温, 湿度 """ d: typ.Tuple = self.bme280.read() store: db_store.DBStore = db_store.DBStore(self.db_url) store.bme280_log(d[1], d[0], d[2]) del store self.add_zabbix("temperature", d[1]) self.add_zabbix("pressure", d[0]) self.add_zabbix("humidity", d[2]) return d
def log_temp(self) -> float: """温度を記録する. Returns: CPU温度 """ with open("/sys/class/thermal/thermal_zone0/temp", "r") as f: temp: int = int(f.readline()) store: db_store.DBStore = db_store.DBStore(self.db_url) store.temp_log(temp) del store self.add_zabbix("cpu_temperature", temp) return float(temp)
def log_co2(self) -> typ.Tuple[int, float]: """CO2を記録する. Returns: CO2濃度, 気温 """ d: typ.Dict = mh_z19.read_all(serial_console_untouched=True) self.sk.debug_print(f"MH_Z19: {d}") if "co2" in d: store: db_store.DBStore = db_store.DBStore(self.db_url) store.co2_log(d["co2"], d["temperature"], d["UhUl"], d["SS"]) del store self.add_zabbix("co2", d["co2"]) return (d["co2"], d["temperature"]) return (0, 0)
def main() -> None: """メイン処理.""" parser: argparse.ArgumentParser = argparse.ArgumentParser() parser.add_argument("-o", "--output", help="output filename") parser.add_argument("-s", "--start", help="start time") parser.add_argument("-e", "--end", help="end time") parser.add_argument("-d", "--days", type=int, help="before n days") parser.add_argument("-w", "--window", type=int, help="window size of moving average", default=30) args: argparse.Namespace = parser.parse_args() today: datetime.date = datetime.date.today() start_time: datetime.datetime = datetime.datetime.combine( today, datetime.time()) if args.start: start_time = datetime.datetime.fromisoformat(args.start) end_time: datetime.datetime = start_time + datetime.timedelta(days=1) if args.end: end_time = datetime.datetime.fromisoformat(args.end) if args.days: end_time = datetime.datetime.now() start_time = end_time - datetime.timedelta(args.days) output_file: str = f"power_{start_time.date()}.html" if args.output: if os.path.isdir(args.output): output_file = os.path.join(args.output, output_file) else: output_file = args.output inifile: configparser.ConfigParser = configparser.ConfigParser() inifile.read("power_consumption.ini", "utf-8") db_url: str = inifile.get("routeB", "db_url") store: db_store.DBStore = db_store.DBStore(db_url) data: typ.List = store.select_power_log(start_time, end_time) make_power_graph(output_file, data, args.window) print(output_file)
def main() -> None: """メイン処理.""" parser: argparse.ArgumentParser = argparse.ArgumentParser() parser.add_argument("-o", "--output", help="output filename") parser.add_argument("-s", "--start", help="start time") parser.add_argument("-e", "--end", help="end time") parser.add_argument("-d", "--days", type=int, help="before n days") args: argparse.Namespace = parser.parse_args() today: datetime.date = datetime.date.today() start_time: datetime.datetime = datetime.datetime.combine( today, datetime.time()) if args.start: start_time = datetime.datetime.fromisoformat(args.start) end_time: datetime.datetime = start_time + datetime.timedelta(days=1) if args.end: end_time = datetime.datetime.fromisoformat(args.end) if args.days: end_time = datetime.datetime.now() start_time = end_time - datetime.timedelta(args.days) output_file: str = f"temp_{start_time.date()}.html" if args.output: if os.path.isdir(args.output): output_file = os.path.join(args.output, output_file) else: output_file = args.output inifile: configparser.ConfigParser = configparser.ConfigParser() inifile.read("power_consumption.ini", "utf-8") db_url: str = inifile.get("routeB", "db_url") store: db_store.DBStore = db_store.DBStore(db_url) temp_data: typ.List = store.select_temp_log(start_time, end_time) co2_data: typ.List = store.select_co2_log(start_time, end_time) bme280_data: typ.List = store.select_bme280_log(start_time, end_time) if len(temp_data) > 0 or len(co2_data) > 0 or len(bme280_data) > 0: make_temp_graph(output_file, temp_data, co2_data, bme280_data) print(output_file) else: print("no data")
def main() -> None: """メイン処理.""" parser: argparse.ArgumentParser = argparse.ArgumentParser() parser.add_argument("-o", "--output", help="output filename") args: argparse.Namespace = parser.parse_args() output_file: str = f"latest.html" if args.output: if os.path.isdir(args.output): output_file = os.path.join(args.output, output_file) else: output_file = args.output inifile: configparser.ConfigParser = configparser.ConfigParser() inifile.read("power_consumption.ini", "utf-8") db_url: str = inifile.get("routeB", "db_url") store: db_store.DBStore = db_store.DBStore(db_url) data: typ.Dict = store.select_latest_log() with open(output_file, "w", newline="\r\n") as out: out.writelines([ "<!DOCTYPE html>\n", "<html lang='ja'>\n", "<head>\n", "<meta charset='utf-8'/>\n", "<meta name='viewport' content='width=device-width, initial-scale=1.0, user-scalable=yes'>\n", "<style type='text/css'>\n", "body { font-size: x-large; }\n", ".red { color: red; }\n", ".blue { color: blue; }\n", ".green { color: green; }\n", ".right { text-align: right; }\n", "</style>\n", "</head>\n", "<body>\n", "<table>\n" ]) if data["power"] is not None: created_at: str = data["power"]["created_at"].strftime("%Y/%m/%d %H:%M:%S") 瞬時電力: int = data["power"]["瞬時電力"] 瞬時電流: float = (data["power"]["瞬時電流_r"] + data["power"]["瞬時電流_t"]) / 10 電流_color: str = "" if 瞬時電流 > 28: 電流_color = "red" elif 瞬時電流 > 15: 電流_color = "blue" out.write(f"<tr><td colspan=3>{created_at}</td></tr>\n") out.write(f"<tr><td>瞬時電力</td><td class='right'>{瞬時電力}</td><td>[W]</td></tr>\n") out.write(f"<tr><td>瞬時電流</td><td class='right {電流_color}'>{瞬時電流}</td><td>[A]</td></tr>\n") if data["temp"] is not None: CPU: float = data["temp"]["temp"] / 1000 out.write(f"<tr><td>CPU温度</td><td class='right'>{CPU:.1f}</td><td>[℃]</td></tr>\n") if data["co2"] is not None: CO2: int = data["co2"]["co2"] co2_color: str = "blue" if CO2 > 2000: co2_color = "red" elif CO2 > 1000: co2_color = "green" #temp: int = data["co2"]["temp"] out.write(f"<tr><td>CO₂濃度</td><td class='right {co2_color}'>{CO2}</td><td>[ppm]</td></tr>\n") #out.write(f"<tr><td>気温</td><td class='right'>{temp}</td><td>[℃]</td></tr>\n") if data["bme280"] is not None: temp: float = data["bme280"]["temp"] hum: float = data["bme280"]["humidity"] pres: float = data["bme280"]["pressure"] temp_color: str = "green" if temp < 18: temp_color = "blue" elif temp > 25: temp_color = "red" hum_color: str = "green" if hum < 40: hum_color = "blue" elif hum > 65: hum_color = "red" out.write(f"<tr><td>気温</td><td class='right {temp_color}'>{temp:.1f}</td><td>[℃]</td></tr>\n") out.write(f"<tr><td>湿度</td><td class='right {hum_color}'>{hum:.1f}</td><td>[%]</td></tr>\n") out.write(f"<tr><td>気圧</td><td class='right'>{pres:.1f}</td><td>[hPa]</td></tr>\n") out.writelines([ "</table>\n" "</body>\n", "</html>\n", ])