Compare commits
12 Commits
tomasztorc
...
tomasztorc
| Author | SHA1 | Date | |
|---|---|---|---|
|
95e856f5f7
|
|||
|
99f0c12455
|
|||
|
6b2ad223c7
|
|||
|
0330fddaf1
|
|||
|
399cce6a2e
|
|||
|
94be339a0a
|
|||
|
5069cfa039
|
|||
|
7ef9c4e614
|
|||
|
17e8a90f40
|
|||
|
edcde52192
|
|||
|
15d2634186
|
|||
|
09da459a53
|
24
.gitea/workflows/build-push-container.yaml
Normal file
24
.gitea/workflows/build-push-container.yaml
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
name: Build and push container
|
||||
run-name: Container creation
|
||||
# change later for only tags
|
||||
on: [push]
|
||||
jobs:
|
||||
Container-Build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Get the code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: |
|
||||
src
|
||||
|
||||
- name: Build & push image ${{ gitea.ref }}
|
||||
uses: bymarshall/kaniko-action@main
|
||||
with:
|
||||
image: ha-addon-energa-meter
|
||||
tag: ${{ gitea.ref }}
|
||||
registry: pipebreaker.pl:5000
|
||||
file: Containerfile
|
||||
context: src/
|
||||
debug_mode: true
|
||||
22
.gitea/workflows/demo.yaml.disabled
Normal file
22
.gitea/workflows/demo.yaml.disabled
Normal file
@@ -0,0 +1,22 @@
|
||||
name: Gitea Actions Demo
|
||||
run-name: ${{ gitea.actor }} is testing out GA 🍆
|
||||
on: [push]
|
||||
jobs:
|
||||
Explore-Gitea-Actions:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: echo "🎉 The job was automatically triggered by a ${{ gitea.event_name }} event."
|
||||
- run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by Gitea!"
|
||||
- run: echo "🔎 The name of your branch is ${{ gitea.ref }} and your repository is ${{ gitea.repository }}."
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
- run: echo "💡 The ${{ gitea.repository }} repository has been cloned to the runner."
|
||||
- run: echo "🖥️ The workflow is now ready to test your code on the runner."
|
||||
- name: List files
|
||||
run: |
|
||||
echo ${{ gitea.workspace }}:
|
||||
ls ${{ gitea.workspace }}
|
||||
- run: echo "🍏 This job's status is ${{ gitea.status }}."
|
||||
|
||||
|
||||
|
||||
23
src/Containerfile
Normal file
23
src/Containerfile
Normal file
@@ -0,0 +1,23 @@
|
||||
FROM fedora-minimal:latest
|
||||
|
||||
RUN microdnf install -y python3-pip && pip install uv
|
||||
|
||||
WORKDIR /app
|
||||
COPY requirements.txt .
|
||||
|
||||
RUN uv venv /app/venv && \
|
||||
source /app/venv/bin/activate && \
|
||||
uv pip install -r requirements.txt
|
||||
|
||||
# Copy data for add-on
|
||||
COPY database_empty.sqlite data/database.sqlite
|
||||
COPY rund.sh .
|
||||
COPY run.py .
|
||||
COPY main.py .
|
||||
COPY api.py .
|
||||
COPY moj_licznik.py .
|
||||
COPY log_config.py .
|
||||
RUN chmod a+x rund.sh
|
||||
|
||||
|
||||
CMD [ "/app/rund.sh" ]
|
||||
@@ -13,7 +13,7 @@ COPY main.py .
|
||||
COPY api.py .
|
||||
COPY moj_licznik.py .
|
||||
COPY log_config.py .
|
||||
COPY database_empty.sqlite data/database.sqlite
|
||||
COPY database_empty.sqlite database.sqlite
|
||||
RUN chmod a+x run.sh
|
||||
|
||||
RUN apk add --update --no-cache py3-pip && \
|
||||
|
||||
@@ -47,11 +47,6 @@ Wymagane parametry:
|
||||
* ENERGA_USERNAME - nazwa użytkownika w aplikacji Energa Mój licznik
|
||||
* ENERGA_PASSWORD - hasło użytkownika w aplikacji Energa Mój licznik
|
||||
|
||||
Opcjonalne parametry:
|
||||
|
||||
* POSTGRESQL_CONNSTRING - namiar na bazę PostgreSQL do przechowywania odczytów;
|
||||
format opisany w [dokumentacji PGSQL](https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING-URIS)
|
||||
(przykładowo `postgresql://uzytkownik:haslo@serwer/mojlicznik`)
|
||||
|
||||
## Konfiguracja sensorów
|
||||
Do HA możesz dodać sensory, które zawierają informacje udostępniane przez API
|
||||
|
||||
33
src/api.py
33
src/api.py
@@ -1,4 +1,4 @@
|
||||
from peewee import SqliteDatabase, PostgresqlDatabase
|
||||
from peewee import SqliteDatabase
|
||||
from flask import Flask, jsonify, request, redirect, url_for, abort
|
||||
from waitress import serve
|
||||
#from datetime
|
||||
@@ -9,30 +9,13 @@ import urllib.parse
|
||||
|
||||
logger = logging.getLogger("energaMeter.api")
|
||||
|
||||
if postgresql_connstring := os.getenv("POSTGRESQL_CONNSTRING"):
|
||||
from psycopg2.extensions import parse_dsn
|
||||
db_name = parse_dsn(postgresql_connstring)['dbname']
|
||||
db = PostgresqlDatabase(db_name, dsn=postgresql_connstring)
|
||||
else:
|
||||
path = os.path.dirname(os.path.abspath(__file__))
|
||||
db_file = 'data/database.sqlite'
|
||||
db = SqliteDatabase(os.path.join(path, db_file))
|
||||
|
||||
path = os.path.dirname(os.path.abspath(__file__))
|
||||
db_file = 'database.sqlite'
|
||||
db = SqliteDatabase(os.path.join(path, db_file))
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
# This hook ensures that a connection is opened to handle any queries
|
||||
# generated by the request.
|
||||
@app.before_request
|
||||
def _db_connect():
|
||||
db.connect()
|
||||
|
||||
# This hook ensures that the connection is closed when we've finished
|
||||
# processing the request.
|
||||
@app.teardown_request
|
||||
def _db_close(exc):
|
||||
if not db.is_closed():
|
||||
db.close()
|
||||
|
||||
@app.route('/', methods=['GET'])
|
||||
def root():
|
||||
query = PPETable.select() #.where(PPETable.is_active == True)
|
||||
@@ -224,7 +207,7 @@ def charts():
|
||||
zone = request.args.get('zone', None)
|
||||
negative = request.args.get('negative', type=bool, default=False)
|
||||
logger.debug(f"API: GET /charts - {start_date} - {end_date}")
|
||||
query = MainChartTable.select().where((MainChartTable.tm >= int(start_date)/1000) & (MainChartTable.tm <= int(end_date)/1000))
|
||||
query = MainChartTable.select().where((MainChartTable.tm >= int(start_date)) & (MainChartTable.tm <= int(end_date)))
|
||||
logger.debug(f"{query}")
|
||||
factor = 1
|
||||
if negative:
|
||||
@@ -243,13 +226,13 @@ def charts():
|
||||
charts = []
|
||||
|
||||
for p in result_ppes:
|
||||
czas = p.tm.strftime("%Y-%m-%d %H:%M:%S")
|
||||
czas = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(p.tm/1000))
|
||||
chart = {
|
||||
'mp': p.mp,
|
||||
'meter_type': p.meter_type,
|
||||
'meter_type_url': urllib.parse.quote_plus(p.meter_type),
|
||||
'zone': p.zone,
|
||||
'time_tm': int(p.tm.timestamp()*1000),
|
||||
'time_tm': p.tm,
|
||||
'time': czas,
|
||||
'value': p.value * factor
|
||||
}
|
||||
|
||||
@@ -1,25 +1,18 @@
|
||||
from peewee import SqliteDatabase, PostgresqlDatabase
|
||||
from peewee import SqliteDatabase
|
||||
from datetime import datetime, timedelta, date
|
||||
import calendar, requests, re, time, json, os, logging
|
||||
import http.cookiejar as cookiejar
|
||||
from requests.exceptions import HTTPError
|
||||
from bs4 import BeautifulSoup
|
||||
from enum import Enum
|
||||
from peewee import AutoField, Model, CharField, IntegerField, DateField, BooleanField, CompositeKey, DecimalField, ForeignKeyField, SQL, TextField, TimestampField
|
||||
from peewee import AutoField, Model, CharField, IntegerField, DateField, BooleanField, CompositeKey, DecimalField, ForeignKeyField, SQL
|
||||
import urllib.parse
|
||||
|
||||
logger = logging.getLogger("energaMeter")
|
||||
|
||||
if postgresql_connstring := os.getenv("POSTGRESQL_CONNSTRING"):
|
||||
from psycopg2.extensions import parse_dsn
|
||||
db_name = parse_dsn(postgresql_connstring)['dbname']
|
||||
db_host = parse_dsn(postgresql_connstring)['host']
|
||||
db = PostgresqlDatabase(db_name, dsn=postgresql_connstring)
|
||||
logger.info(f"Używam bazy PostgreSQL „{db_name}” na {db_host}")
|
||||
else:
|
||||
path = os.path.dirname(os.path.abspath(__file__))
|
||||
db_file = 'data/database.sqlite'
|
||||
db = SqliteDatabase(os.path.join(path, db_file))
|
||||
path = os.path.dirname(os.path.abspath(__file__))
|
||||
db_file = 'database.sqlite'
|
||||
db = SqliteDatabase(os.path.join(path, db_file))
|
||||
|
||||
class ChartType(Enum):
|
||||
DAY = "DAY"
|
||||
@@ -38,7 +31,7 @@ class PPETable(Model):
|
||||
class Meta:
|
||||
database = db
|
||||
table_name = 'PPE'
|
||||
constraints = [SQL('UNIQUE ("ppe", "tariffCode")')]
|
||||
constraints = [SQL('UNIQUE (ppe, tariffCode)')]
|
||||
|
||||
class MeterTable(Model):
|
||||
id = AutoField() # Meter point
|
||||
@@ -50,7 +43,7 @@ class MeterTable(Model):
|
||||
class Meta:
|
||||
database = db
|
||||
table_name = 'METER'
|
||||
constraints = [SQL('UNIQUE ("ppe_id", "meter_type")')]
|
||||
constraints = [SQL('UNIQUE (ppe_id, meter_type)')]
|
||||
|
||||
class CounterTable(Model):
|
||||
id = AutoField()
|
||||
@@ -78,7 +71,7 @@ class ChartTable(Model):
|
||||
year = IntegerField()
|
||||
month = IntegerField(null=True)
|
||||
day = IntegerField(null=True)
|
||||
value = TextField()
|
||||
value =CharField()
|
||||
|
||||
class Meta:
|
||||
database = db
|
||||
@@ -89,7 +82,7 @@ class MainChartTable(Model):
|
||||
mp = CharField()
|
||||
meter_type = CharField()
|
||||
zone = IntegerField()
|
||||
tm = TimestampField()
|
||||
tm = IntegerField()
|
||||
value = DecimalField(max_digits=20, decimal_places=16, null=True)
|
||||
tarAvg = DecimalField(max_digits=20, decimal_places=16, null=True)
|
||||
est = BooleanField(default=False)
|
||||
@@ -367,17 +360,16 @@ class MojLicznik:
|
||||
try:
|
||||
logger.debug(f"save_main_charts: mp: {mp}, val: {val}, meter_type: {m_type}")
|
||||
z = val["zones"]
|
||||
tm = int(val["tm"]) / 1000 # convert JS timestamp (milliseconds) to unix (seconds)
|
||||
if z[0]:
|
||||
# MainChartTable.get_or_create(tm = val["tm"], zone = 1, value = z[0], tarAvg=val["tarAvg"], est=val["est"], cplt=val["cplt"])
|
||||
try:
|
||||
existing_record = MainChartTable.get((MainChartTable.meter_type == m_type) & (MainChartTable.mp == mp) & (MainChartTable.tm == tm) & (MainChartTable.zone == 1))
|
||||
existing_record = MainChartTable.get((MainChartTable.meter_type == m_type) & (MainChartTable.mp == mp) & (MainChartTable.tm == val["tm"]) & (MainChartTable.zone == 1))
|
||||
except MainChartTable.DoesNotExist:
|
||||
# Jeśli rekord nie istnieje, utwórz nowy
|
||||
MainChartTable.create(
|
||||
mp=mp,
|
||||
meter_type=m_type,
|
||||
tm=tm,
|
||||
tm=val["tm"],
|
||||
zone=1,
|
||||
value=z[0],
|
||||
tarAvg=val["tarAvg"],
|
||||
@@ -387,13 +379,13 @@ class MojLicznik:
|
||||
|
||||
if z[1]:
|
||||
try:
|
||||
existing_record = MainChartTable.get((MainChartTable.meter_type == m_type) & (MainChartTable.mp == mp) & (MainChartTable.tm == tm) & (MainChartTable.zone == 2))
|
||||
existing_record = MainChartTable.get((MainChartTable.meter_type == m_type) & (MainChartTable.mp == mp) & (MainChartTable.tm == val["tm"]) & (MainChartTable.zone == 2))
|
||||
except MainChartTable.DoesNotExist:
|
||||
# Jeśli rekord nie istnieje, utwórz nowy
|
||||
MainChartTable.create(
|
||||
mp=mp,
|
||||
meter_type=m_type,
|
||||
tm=tm,
|
||||
tm=val["tm"],
|
||||
zone=2,
|
||||
value=z[1],
|
||||
tarAvg=val["tarAvg"],
|
||||
@@ -403,13 +395,13 @@ class MojLicznik:
|
||||
|
||||
if z[2]:
|
||||
try:
|
||||
existing_record = MainChartTable.get((MainChartTable.meter_type == m_type) & (MainChartTable.mp == mp) & (MainChartTable.tm == tm) & (MainChartTable.zone == 3))
|
||||
existing_record = MainChartTable.get((MainChartTable.meter_type == m_type) & (MainChartTable.mp == mp) & (MainChartTable.tm == val["tm"]) & (MainChartTable.zone == 3))
|
||||
except MainChartTable.DoesNotExist:
|
||||
# Jeśli rekord nie istnieje, utwórz nowy
|
||||
MainChartTable.create(
|
||||
mp=mp,
|
||||
meter_type=m_type,
|
||||
tm=tm,
|
||||
tm=val["tm"],
|
||||
zone=3,
|
||||
value=z[2],
|
||||
tarAvg=val["tarAvg"],
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user