Skip to content

Commit

Permalink
web: Add chart to overview system
Browse files Browse the repository at this point in the history
  • Loading branch information
hoang-rio committed Nov 27, 2024
1 parent 0d7cda2 commit 4173683
Show file tree
Hide file tree
Showing 26 changed files with 476 additions and 153 deletions.
93 changes: 77 additions & 16 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,24 +112,26 @@ def handle_grid_status(json_data: dict, fcm_service: FCM):
logger.info("State did not change. Skip play notify audio")


def insert_daily_chart(db_connection: sqlite3.Connection, inverter_data: dict):
def insert_hourly_chart(db_connection: sqlite3.Connection, inverter_data: dict):
cursor = db_connection.cursor()
device_time = datetime.strptime(inverter_data["deviceTime"],
"%Y-%m-%d %H:%M:%S")
sleep_time = int(config["SLEEP_TIME"])
if sleep_time < 60:
if device_time.hour == 0 and device_time.minute == 0 and device_time.second <= sleep_time:
cursor.execute("DELETE FROM daily_chart WHERE datetime < ?", (inverter_data["deviceTime"],))
cursor.execute(
"DELETE FROM hourly_chart WHERE datetime < ?", (inverter_data["deviceTime"],))
elif sleep_time < 3600:
if device_time.hour == 0 and device_time.minute <= sleep_time / 60:
cursor.execute("DELETE FROM daily_chart WHERE datetime < ?", (inverter_data["deviceTime"],))
cursor.execute(
"DELETE FROM hourly_chart WHERE datetime < ?", (inverter_data["deviceTime"],))

item_id = device_time.strftime("%Y%m%d%H%M")
grid = inverter_data["p_to_user"] - inverter_data["p_to_grid"]
consumption = inverter_data["p_inv"] + \
inverter_data["p_to_user"] - \
inverter_data["p_rec"]
daily_chart_item = {
hourly_chart_item = {
"id": item_id,
"datetime": inverter_data["deviceTime"],
"pv": inverter_data["p_pv"],
Expand All @@ -139,30 +141,84 @@ def insert_daily_chart(db_connection: sqlite3.Connection, inverter_data: dict):
"soc": inverter_data["soc"],
}
is_exist = cursor.execute(
"SELECT id FROM daily_chart WHERE id = ?", (
"SELECT id FROM hourly_chart WHERE id = ?", (
item_id,)
).fetchone()
if is_exist is None:
cursor.execute(
"INSERT INTO daily_chart (id, datetime, pv, battery, grid, consumption, soc) VALUES (?, ?, ?, ?, ?, ?, ?)",
(item_id, daily_chart_item["datetime"], daily_chart_item["pv"], daily_chart_item["battery"],
daily_chart_item["grid"], daily_chart_item["consumption"], daily_chart_item["soc"]),
"INSERT INTO hourly_chart (id, datetime, pv, battery, grid, consumption, soc) VALUES (?, ?, ?, ?, ?, ?, ?)",
(item_id, hourly_chart_item["datetime"], hourly_chart_item["pv"], hourly_chart_item["battery"],
hourly_chart_item["grid"], hourly_chart_item["consumption"], hourly_chart_item["soc"]),
)
else:
cursor.execute(
"UPDATE hourly_chart SET datetime = ?, pv = ?, battery = ?, grid = ?, consumption = ?, soc = ? WHERE id = ?",
(
hourly_chart_item["datetime"],
hourly_chart_item["pv"],
hourly_chart_item["battery"],
hourly_chart_item["grid"],
hourly_chart_item["consumption"],
hourly_chart_item["soc"],
item_id)
)
cursor.close()
db_connection.commit()
return [item_id, hourly_chart_item["datetime"], hourly_chart_item["pv"], hourly_chart_item["battery"], hourly_chart_item["grid"], hourly_chart_item["consumption"], hourly_chart_item["soc"]]


def insert_daly_chart(db_connection: sqlite3.Connection, inverter_data: dict):
cursor = db_connection.cursor()
device_time = datetime.strptime(inverter_data["deviceTime"],
"%Y-%m-%d %H:%M:%S")
item_id = device_time.strftime("%Y%m%d")
consumption = (
inverter_data["e_inv_day"] +
inverter_data["e_to_user_day"] +
inverter_data["e_eps_day"] -
inverter_data["e_rec_day"]
)
daily_chart_item = {
"id": item_id,
"year": device_time.year,
"month": device_time.month,
"date": device_time.strftime("%Y-%m-%d"),
"pv": inverter_data["e_pv_day"],
"battery_charged": inverter_data["e_chg_day"],
"battery_discharged": inverter_data["e_dischg_day"],
"grid_import": inverter_data["e_to_user_day"],
"grid_export": inverter_data["e_to_grid_day"],
"consumption": round(consumption, 1),
}
is_exist = cursor.execute(
"SELECT id FROM daily_chart WHERE id = ?", (item_id,)
).fetchone()
if is_exist is None:
cursor.execute(
"INSERT INTO daily_chart (id, date, pv, battery_charged, battery_discharged, grid_import, grid_export, consumption) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
(item_id, daily_chart_item["year"], daily_chart_item["month"], daily_chart_item["date"], daily_chart_item["pv"], daily_chart_item["battery_charged"],
daily_chart_item["battery_discharged"], daily_chart_item["grid_import"], daily_chart_item["grid_export"],
daily_chart_item["consumption"]),
)
else:
cursor.execute(
"UPDATE daily_chart SET datetime = ?, pv = ?, battery = ?, grid = ?, consumption = ?, soc = ? WHERE id = ?",
"UPDATE daily_chart SET year = ?, month = ?, date = ?, pv = ?, battery_charged = ?, battery_discharged = ?, grid_import = ?, grid_export = ?, consumption = ? WHERE id = ?",
(
daily_chart_item["datetime"],
daily_chart_item["year"],
daily_chart_item["month"],
daily_chart_item["date"],
daily_chart_item["pv"],
daily_chart_item["battery"],
daily_chart_item["grid"],
daily_chart_item["battery_charged"],
daily_chart_item["battery_discharged"],
daily_chart_item["grid_import"],
daily_chart_item["grid_export"],
daily_chart_item["consumption"],
daily_chart_item["soc"],
item_id)
)
cursor.close()
db_connection.commit()


async def main():
try:
logger.info("Grid connect watch working on mode: %s",
Expand All @@ -181,7 +237,8 @@ async def main():
webViewer.start()
time.sleep(1)
from web_socket_client import WebSocketClient
ws_client = WebSocketClient(logger=logger, host=config["HOST"], port=int(config["PORT"]))
ws_client = WebSocketClient(
logger=logger, host=config["HOST"], port=int(config["PORT"]))
ws_client.start()
dongle = dongle_handler.Dongle(logger, config)
while True:
Expand All @@ -190,8 +247,12 @@ async def main():
handle_grid_status(inverter_data, fcm_service)
if run_web_view:
if db_connection is not None:
insert_daily_chart(db_connection, inverter_data)
await ws_client.send_json(inverter_data)
hourly_chart_item = insert_hourly_chart(db_connection, inverter_data)
insert_daly_chart(db_connection, inverter_data)
await ws_client.send_json({
"inverter_data": inverter_data,
"hourly_chart_item": hourly_chart_item
})
logger.info("Wating for %s second before next check",
config["SLEEP_TIME"])
time.sleep(int(config["SLEEP_TIME"]))
Expand Down
4 changes: 2 additions & 2 deletions migration.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@

MIGRATIONS_SQL = [
"CREATE TABLE IF NOT EXISTS migration (id INTEGER PRIMARY KEY, applied_at TEXT)",
"CREATE TABLE IF NOT EXISTS daily_chart (id VARCHAR PRIMARY KEY, datetime TEXT, pv INTEGER, battery INTEGER, grid INTEGER, consumption INTEGER, soc INTERGER)",
"CREATE TABLE IF NOT EXISTS hourly_chart (id VARCHAR PRIMARY KEY, datetime TEXT, pv INTEGER, battery INTEGER, grid INTEGER, consumption INTEGER, soc INTERGER)",
"CREATE TABLE IF NOT EXISTS daily_chart (id VARCHAR PRIMARY KEY, year INTEGER, month INTEGER, date TEXT, pv INTEGER, battery_charged INTEGER, battery_discharged INTEGER, grid_import INTEGER, grid_export INTEGER, consumption INTEGER)",
]
def execute_migration_sql(id: int, sql: str, cursor: sqlite3.Cursor) -> None:
global logger
Expand Down Expand Up @@ -66,7 +67,6 @@ def run_migration(db_connection: sqlite3.Connection | None = None, _logger: logg
execute_migration_sql(id, sql, cursor)
conn.commit()
cursor.close()
conn.close()

if __name__ == '__main__':
run_migration()
15 changes: 14 additions & 1 deletion web_viewer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,19 @@ async def websocket_handler(request):
def state(_: web.Request):
global last_inverter_data
res = web.json_response(json.loads(
last_inverter_data), headers=cors_header)
last_inverter_data)["inverter_data"], headers=cors_header)
return res

def hourly_chart(_: web.Request):
if "DB_NAME" not in config:
return web.json_response([], headers=cors_header)
global db_connection
if db_connection is None:
db_connection = sqlite3.connect(config["DB_NAME"])
cursor = db_connection.cursor()
hourly_chart = cursor.execute(
"SELECT * FROM hourly_chart").fetchall()
res = web.json_response(hourly_chart, headers=cors_header)
return res

def daily_chart(_: web.Request):
Expand All @@ -78,6 +90,7 @@ def create_runner():
web.get("/", http_handler),
web.get("/ws", websocket_handler),
web.get("/state", state),
web.get("/hourly-chart", hourly_chart),
web.get("/daily-chart", daily_chart),
web.static("/", path.join(path.dirname(__file__), "public"))
])
Expand Down
14 changes: 6 additions & 8 deletions web_viewer/fe_src/src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,12 @@
.description {
color: #d7d7d7;
}

.text-right {
text-align: right;
}
@keyframes logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

.chart {
display: flex;
flex-wrap: wrap;
}
17 changes: 12 additions & 5 deletions web_viewer/fe_src/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { useCallback, useEffect, useRef, useState, lazy } from "react";
import "./App.css";
import { IInverterData } from "./Intefaces";
import SystemInformation from "./components/SystemInformation";
import Summary from "./components/Summary";
import { IUpdateChart, IInverterData } from "./Intefaces";

const SystemInformation = lazy(() => import("./components/SystemInformation"));
const Summary = lazy(() => import("./components/Summary"));
const HourlyChart = lazy(() => import("./components/HourlyChart"));
const DailyChart = lazy(() => import("./components/DailyChart"));

const MAX_RECONNECT_COUNT = 3;
Expand All @@ -13,6 +15,7 @@ function App() {
const selfCloseRef = useRef<boolean>(false);
const reconnectCountRef = useRef<number>(0);
const [isSocketConnected, setIsSocketConnected] = useState<boolean>(true);
const hourlyCharfRef = useRef<IUpdateChart>(null);

const connectSocket = useCallback(() => {
if (
Expand All @@ -36,7 +39,8 @@ function App() {
// Listen for messages
socket.addEventListener("message", (event) => {
const jsonData = JSON.parse(event.data);
setInverterData(jsonData);
setInverterData(jsonData.inverter_data);
hourlyCharfRef.current?.updateItem(jsonData.hourly_chart_item);
});
socket.addEventListener("close", () => {
document.title = `[Offline] ${import.meta.env.VITE_APP_TITLE}`;
Expand Down Expand Up @@ -110,7 +114,10 @@ function App() {
isSocketConnected={isSocketConnected}
onReconnect={connectSocket}
/>
<DailyChart />
<div className="row chart">
<HourlyChart ref={hourlyCharfRef} className="flex-1" />
<DailyChart className="flex-1" />
</div>
</>
);
}
Expand Down
13 changes: 13 additions & 0 deletions web_viewer/fe_src/src/Intefaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,17 @@ export interface IInverterData {
export interface ICProps {
inverterData: IInverterData;
isSocketConnected: boolean;
}

export interface SeriesItem {
x: number | string;
y: never;
}

export interface IClassNameProps {
className?: string;
}

export interface IUpdateChart {
updateItem: (hourlyItem: never[]) => void;
}
4 changes: 4 additions & 0 deletions web_viewer/fe_src/src/components/DailyChart.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@
.daily-chart-title {
font-weight: bold;
}

.daily-chart-content {
margin-top: 10px;
}
Loading

0 comments on commit 4173683

Please sign in to comment.