diff --git a/README.md b/README.md index 24f3200..02778d1 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@

mini-graph-card +mini-graph-card

本应用可以帮助你将国网的电费、用电量数据接入homeassistant,实现实时追踪家庭用电量情况;并且可以将每日用电量保存到数据库,历史有迹可循。具体提供两类数据: @@ -20,6 +21,8 @@ | sensor.electricity_charge_balance | 预付费显示电费余额,反之显示上月应交电费,单位元 | | sensor.yearly_electricity_usage | 今年总用电量,单位KWH、度。 | | sensor.yearly_electricity_charge | 今年总用电费用,单位元 | + | sensor.month_electricity_usage | 最近一天用电量,单位KWH、度。属性含present_date(查询电量代表的日期)| + | sensor.month_electricity_charge | 上月总用电费用,单位元 属性含present_date(查询电量代表的日期)| | 2. 可选,近三十天每日用电量数据(mongodb数据库),例如: ![image-20230731094609016](assets/image-20230731111508970.png) @@ -162,7 +165,7 @@ ```bash git clone https://github.com/ARC-MX/sgcc_electricity_new.git - cd sgcc_electricity + cd sgcc_electricity_new ``` 2. 参考example.env编写.env文件 @@ -180,7 +183,7 @@ ### 3)方法三:不安装docker,安装python环境后直接运行: -克隆仓库之后,参考Dockerfile的命令,``自行配置安装chrome浏览器和selenium浏览器驱动``,安装mongodb,将example.env文件复制为.env文件到scripts文件夹下,然后运行main.py文件。 +克隆仓库之后,参考Dockerfile的命令,``自行配置安装chrome浏览器和selenium浏览器驱动 ``,安装mongodb,将example.env文件复制为.env文件到scripts文件夹下,然后运行main.py文件。 ### 4)方法四:使用可视化docker管理工具[portainer]([url](https://www.portainer.io/))部署: @@ -198,7 +201,6 @@ - 如果你有一个户号,参照以下配置: -```yaml # Example configuration.yaml entry # 文件中只能有一个template template: @@ -215,7 +217,7 @@ template: state_class: total unit_of_measurement: "CNY" device_class: monetary - + - trigger: - platform: event event_type: "state_changed" @@ -231,7 +233,39 @@ template: state_class: measurement unit_of_measurement: "kWh" device_class: energy - + + - trigger: + - platform: event + event_type: "state_changed" + event_data: + entity_id: sensor.month_electricity_usage + sensor: + - name: month_electricity_usage_entity + unique_id: month_electricity_usage_entity + state: "{{ states('sensor.month_electricity_usage') }}" + attributes: + present_date: "{{ state_attr('sensor.month_electricity_usage', 'present_date') }}" + last_updated: "{{ state_attr('sensor.month_electricity_usage', 'month_updated') }}" + state_class: measurement + unit_of_measurement: "kWh" + device_class: energy + + - trigger: + - platform: event + event_type: "state_changed" + event_data: + entity_id: sensor.month_electricity_charge + sensor: + - name: month_electricity_charge_entity + unique_id: month_electricity_charge_entity + state: "{{ states('sensor.month_electricity_charge') }}" + attributes: + present_date: "{{ state_attr('sensor.month_electricity_charge', 'present_date') }}" + last_updated: "{{ state_attr('sensor.month_electricity_charge', 'month_updated') }}" + state_class: measurement + unit_of_measurement: "CNY" + device_class: monetary + - trigger: - platform: event event_type: "state_changed" @@ -257,7 +291,6 @@ template: state_class: total unit_of_measurement: "CNY" device_class: monetary -``` - 如果你有多个户号,每个户号参照[configuration.yaml](template/configuration.yaml)配置。 diff --git a/assets/image-20240514.jpg b/assets/image-20240514.jpg new file mode 100644 index 0000000..5bbbd9b Binary files /dev/null and b/assets/image-20240514.jpg differ diff --git a/scripts/const.py b/scripts/const.py index c078881..fea5fef 100644 --- a/scripts/const.py +++ b/scripts/const.py @@ -12,7 +12,9 @@ BALANCE_SENSOR_NAME = "sensor.electricity_charge_balance" DAILY_USAGE_SENSOR_NAME = "sensor.last_electricity_usage" YEARLY_USAGE_SENSOR_NAME = "sensor.yearly_electricity_usage" -YEARLY_CHARGE_SENESOR_NAME = "sensor.yearly_electricity_charge" +YEARLY_CHARGE_SENSOR_NAME = "sensor.yearly_electricity_charge" +MONTH_USAGE_SENSOR_NAME = "sensor.month_electricity_usage" +MONTH_CHARGE_SENSOR_NAME = "sensor.month_electricity_charge" BALANCE_UNIT = "CNY" USAGE_UNIT = "KWH" diff --git a/scripts/data_fetcher.py b/scripts/data_fetcher.py index 8b9bb2b..2cdba02 100644 --- a/scripts/data_fetcher.py +++ b/scripts/data_fetcher.py @@ -269,13 +269,13 @@ def _fetch(self): balance_list = self._get_electric_balances(driver, user_id_list) # time.sleep(self.RETRY_WAIT_TIME_OFFSET_UNIT) ### get data except electricity charge balance - last_daily_date_list, last_daily_usage_list, yearly_charge_list, yearly_usage_list = self._get_other_data(driver, user_id_list) + last_daily_date_list, last_daily_usage_list, yearly_charge_list, yearly_usage_list, month_list, month_usage_list, month_charge_list = self._get_other_data(driver, user_id_list) time.sleep(self.RETRY_WAIT_TIME_OFFSET_UNIT) driver.quit() logging.info("Webdriver quit after fetching data successfully.") logging.info("浏览器已退出") - return user_id_list, balance_list, last_daily_date_list, last_daily_usage_list, yearly_charge_list, yearly_usage_list + return user_id_list, balance_list, last_daily_date_list, last_daily_usage_list, yearly_charge_list, yearly_usage_list, month_list, month_usage_list, month_charge_list finally: driver.quit() @@ -380,7 +380,9 @@ def _get_other_data(self, driver, user_id_list): last_daily_usage_list = [] yearly_usage_list = [] yearly_charge_list = [] - + month_list = [] + month_charge_list = [] + month_usage_list = [] # swithc to electricity usage page driver.get(ELECTRIC_USAGE_URL) @@ -400,6 +402,14 @@ def _get_other_data(self, driver, user_id_list): logging.info( f"Get year power charge for {user_id_list[i - 1]} successfully, yealrly charge is {yearly_charge} CNY") + # get month usage + month, month_usage, month_charge = self._get_month_usage(driver) + if month is None: + logging.error(f"Get month power usage for {user_id_list[i - 1]} failed, pass") + else: + for m in range(len(month)): + logging.info( + f"Get month power charge for {user_id_list[i - 1]} successfully, {month[m]} usage is {month_usage[m]} KWh, charge is {month_charge[m]} CNY.") # get yesterday usage last_daily_datetime, last_daily_usage = self._get_yesterday_usage(driver) @@ -417,6 +427,9 @@ def _get_other_data(self, driver, user_id_list): last_daily_usage_list.append(last_daily_usage) yearly_charge_list.append(yearly_charge) yearly_usage_list.append(yearly_usage) + month_list.append(month[-1]) + month_charge_list.append(month_charge[-1]) + month_usage_list.append(month_usage[-1]) # switch to next user id if i != len(user_id_list): @@ -425,7 +438,7 @@ def _get_other_data(self, driver, user_id_list): self._click_button(driver, By.XPATH, f"//body/div[@class='el-select-dropdown el-popper']//ul[@class='el-scrollbar__view el-select-dropdown__list']/li[{i + 1}]") - return last_daily_date_list, last_daily_usage_list, yearly_charge_list, yearly_usage_list + return last_daily_date_list, last_daily_usage_list, yearly_charge_list, yearly_usage_list, month_list, month_usage_list, month_charge_list def _get_user_ids(self, driver): @@ -496,6 +509,31 @@ def _get_yesterday_usage(self, driver): except: return None + def _get_month_usage(self, driver): + """获取每月用电量""" + + try: + self._click_button(driver, By.XPATH, "//div[@class='el-tabs__nav is-top']/div[@id='tab-first']") + time.sleep(self.RETRY_WAIT_TIME_OFFSET_UNIT) + # wait for month displayed + target = driver.find_element(By.CLASS_NAME, "total") + WebDriverWait(driver, self.DRIVER_IMPLICITY_WAIT_TIME).until(EC.visibility_of(target)) + month_element = driver.find_element(By.XPATH, "//*[@id='pane-first']/div[1]/div[2]/div[2]/div/div[3]/table/tbody").text + month_element = month_element.split("\n") + month_element.remove("MAX") + month_element = np.array(month_element).reshape(-1, 3) + # 将每月的用电量保存为List + month = [] + usage = [] + charge = [] + for i in range(len(month_element)): + month.append(month_element[i][0]) + usage.append(month_element[i][1]) + charge.append(month_element[i][2]) + return month, usage, charge + except: + return None,None,None + # 增加储存30天用电量的到mongodb的函数 def save_30_days_usage(self, driver, user_id): """储存30天用电量""" diff --git a/scripts/main.py b/scripts/main.py index 5efd39c..cc427ca 100644 --- a/scripts/main.py +++ b/scripts/main.py @@ -54,20 +54,22 @@ def main(): def run_task(data_fetcher: DataFetcher, sensor_updator: SensorUpdator): try: - user_id_list, balance_list, last_daily_date_list, last_daily_usage_list, yearly_charge_list, yearly_usage_list = data_fetcher.fetch() - + user_id_list, balance_list, last_daily_date_list, last_daily_usage_list, yearly_charge_list, yearly_usage_list, month_list, month_usage_list, month_charge_list = data_fetcher.fetch() + # user_id_list, balance_list, last_daily_date_list, last_daily_usage_list, yearly_charge_list, yearly_usage_list, month_list, month_usage_list, month_charge_list = ['123456'],[58.1],['2024-05-12'],[3.0],['239.1'],['533'],['2024-04-01-2024-04-30'],['118'],['52.93'] for i in range(0, len(user_id_list)): profix = f"_{user_id_list[i]}" if len(user_id_list) > 1 else "" if balance_list[i] is not None: sensor_updator.update(BALANCE_SENSOR_NAME + profix, None, balance_list[i], BALANCE_UNIT) if last_daily_usage_list[i] is not None: - sensor_updator.update(DAILY_USAGE_SENSOR_NAME + profix, last_daily_date_list[i], - last_daily_usage_list[i], USAGE_UNIT) + sensor_updator.update(DAILY_USAGE_SENSOR_NAME + profix, last_daily_date_list[i], last_daily_usage_list[i], USAGE_UNIT) if yearly_usage_list[i] is not None: sensor_updator.update(YEARLY_USAGE_SENSOR_NAME + profix, None, yearly_usage_list[i], USAGE_UNIT) if yearly_charge_list[i] is not None: - sensor_updator.update(YEARLY_CHARGE_SENESOR_NAME + profix, None, yearly_charge_list[i], BALANCE_UNIT) - + sensor_updator.update(YEARLY_CHARGE_SENSOR_NAME + profix, None, yearly_charge_list[i], BALANCE_UNIT) + if month_charge_list[i] is not None: + sensor_updator.update(MONTH_CHARGE_SENSOR_NAME + profix, month_list[i], month_charge_list[i], BALANCE_UNIT, month=True) + if month_usage_list[i] is not None: + sensor_updator.update(MONTH_USAGE_SENSOR_NAME + profix, month_list[i], month_usage_list[i], USAGE_UNIT, month=True) logging.info("state-refresh task run successfully!") except Exception as e: logging.error(f"state-refresh task failed, reason is {e}") diff --git a/scripts/sensor_updator.py b/scripts/sensor_updator.py index 60fc489..60e3f48 100644 --- a/scripts/sensor_updator.py +++ b/scripts/sensor_updator.py @@ -13,7 +13,7 @@ def __init__(self, base_url: str, token: str): self.base_url = base_url[:-1] if base_url.endswith("/") else base_url self.token = token - def update(self, sensorName: str, present_date: str or None, sensorState: float, sensorUnit: str): + def update(self, sensorName: str, present_date: str or None, sensorState: float, sensorUnit: str, month=False): """ Update the sensor state :param sensorName: 此为id,不是name @@ -28,7 +28,10 @@ def update(self, sensorName: str, present_date: str or None, sensorState: float, "Authorization": "Bearer " + token } - last_updated = datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%f%z") + if month: + last_updated = datetime.now().strftime("%Y-%m") + else: + last_updated = datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%f%z") if present_date: request_body = { "state": sensorState,