Skip to content

Commit

Permalink
Option use_utc added. DB schema modified (now using an autoincrement …
Browse files Browse the repository at this point in the history
…key and an index on clock field)
  • Loading branch information
Ailothaen committed Nov 13, 2022
1 parent 03676bf commit fd81723
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 76 deletions.
8 changes: 8 additions & 0 deletions CONFIG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Configuration

**database.server** : Serveur MariaDB
**database.name** : Nom de la base de données
**database.user** : Utilisateur qui va se connecter au serveur MariaDB
**database.password** : Mot de passe de l'utilisateur
**device** : Fichier représentant la connexion série (dans /dev, souvent /dev/ttyS0)
**use_utc** : Si "true", l'heure et la date seront enregistrés en UTC. Si "false", cela sera en heure locale (attention, car cela peut provoquer des heures en double, notamment aux jours de changement d'heure)
113 changes: 59 additions & 54 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,55 +1,60 @@
# linky

(This README is in French only because this subject is specific to France)

Ce dépôt Github contient un script Python permettant de lire les données en provenance du bornier "Téléinformation" d'un compteur Linky.

Pour plus d'informations sur comment brancher et configurer la liaison série, lisez ces articles :
- [Partie 1](https://notes.ailothaen.fr/post/2022/07/Mesures-et-graphiques-de-la-consommation-d-un-compteur-Linky-avec-un-Raspberry-Pi-et-Grafana-%E2%80%93-Partie-1/2-%28mat%C3%A9riel%29)
- [Partie 2](https://notes.ailothaen.fr/post/2022/07/Mesures-et-graphiques-d-un-compteur-Linky-avec-un-Raspberry-Pi-et-Grafana-%E2%80%93-Partie-2/2-%28logiciel%29)

## Dépendances

Pour fonctionner, ce script requiert une base de données type MySQL, ainsi que quelques dépendances Python.

Voici les commandes classiques sur MariaDB pour créer une base de données, créer un utilisateur, et donner tous les droits à cet utilisateur sur cette base de données :

```
mysql> CREATE DATABASE linky;
mysql> CREATE USER 'linky'@'localhost' IDENTIFIED BY 'motdepasse';
mysql> GRANT ALL PRIVILEGES ON linky.* TO 'linky'@'localhost';
mysql> FLUSH PRIVILEGES;
```

Installez les dépendances Python avec `python3 -m pip install -r requirements.txt`


## Mise en place du service

Mettez le fichier `linky.service` dans `/etc/systemd/system` (sur Debian ; l'emplacement est peut-être différent selon la distribution).
Éditez le fichier selon l'endroit où vous mettrez les scripts, et l'utilisateur que vous utiliserez.


## Démarrage

```
systemctl daemon-reload
systemctl enable linky
systemctl start linky
```

Si vous avez bien tout installé, vous devriez commencer à voir des lignes (une ligne par minute) dans la table "stream".

```
MariaDB [linky]> select * from stream;
+---------------------+---------+------+-----------+
| clock | BASE | PAPP | BASE_diff |
+---------------------+---------+------+-----------+
| 2022-07-14 17:02:55 | 2086442 | 70 | 0 |
| 2022-07-14 17:03:57 | 2086443 | 70 | 1 |
| 2022-07-14 17:04:58 | 2086443 | 70 | 0 |
+---------------------+---------+------+-----------+
4 rows in set (0.002 sec)
```

# linky

(This README is in French only because this subject is specific to France)

Ce dépôt Github contient un script Python permettant de lire les données en provenance du bornier "Téléinformation" d'un compteur Linky.

Pour plus d'informations sur comment brancher et configurer la liaison série, lisez ces articles :
- [Partie 1](https://notes.ailothaen.fr/post/2022/07/Mesures-et-graphiques-de-la-consommation-d-un-compteur-Linky-avec-un-Raspberry-Pi-et-Grafana-%E2%80%93-Partie-1/2-%28mat%C3%A9riel%29)
- [Partie 2](https://notes.ailothaen.fr/post/2022/07/Mesures-et-graphiques-d-un-compteur-Linky-avec-un-Raspberry-Pi-et-Grafana-%E2%80%93-Partie-2/2-%28logiciel%29)

## Dépendances

Pour fonctionner, ce script requiert une base de données type MySQL, ainsi que quelques dépendances Python.

Voici les commandes classiques sur MariaDB pour créer une base de données, créer un utilisateur, et donner tous les droits à cet utilisateur sur cette base de données :

```
mysql> CREATE DATABASE linky;
mysql> CREATE USER 'linky'@'localhost' IDENTIFIED BY 'motdepasse';
mysql> GRANT ALL PRIVILEGES ON linky.* TO 'linky'@'localhost';
mysql> FLUSH PRIVILEGES;
```

Installez les dépendances Python avec `python3 -m pip install -r requirements.txt`


## Mise en place du service

Mettez le fichier `linky.service` dans `/etc/systemd/system` (sur Debian ; l'emplacement est peut-être différent selon la distribution).
Éditez le fichier selon l'endroit où vous mettrez les scripts, et l'utilisateur que vous utiliserez.


## Configuration

La configuration se fait dans le fichier config.yml. La documentation sur ce fichier est dans CONFIG.md.


## Démarrage

```
systemctl daemon-reload
systemctl enable linky
systemctl start linky
```

Si vous avez bien tout installé, vous devriez commencer à voir des lignes (une ligne par minute) dans la table "stream".

```
MariaDB [linky]> select * from stream;
+---------------------+---------+------+-----------+
| clock | BASE | PAPP | BASE_diff |
+---------------------+---------+------+-----------+
| 2022-07-14 17:02:55 | 2086442 | 70 | 0 |
| 2022-07-14 17:03:57 | 2086443 | 70 | 1 |
| 2022-07-14 17:04:58 | 2086443 | 70 | 0 |
+---------------------+---------+------+-----------+
4 rows in set (0.002 sec)
```

Sinon, regardez les logs du script (`logs`) ou du service (`journalctl -u linky`)
14 changes: 7 additions & 7 deletions config.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
database:
server: 127.0.0.1
name: linky
user: linky
password: mypassword
device:
file: /dev/ttyS0
database:
server: 127.0.0.1
name: linky
user: linky
password: mypassword
device: /dev/ttyS0
use_utc: True
29 changes: 21 additions & 8 deletions linky.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,13 @@ def test_db_connection(server, user, password, name):
if stream_exists == 0 or dailies_exists == 0:
log.info("Database schema is not there, creating it...")
try:
cr.execute("CREATE TABLE `dailies` (`clock` date NOT NULL, `BASE_diff` int(11) NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;")
cr.execute("CREATE TABLE `stream` (`clock` datetime NOT NULL, `BASE` int(11) NOT NULL, `PAPP` int(11) NOT NULL, `BASE_diff` int(11) NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;")
cr.execute("CREATE TABLE `dailies` (`id` int(10) UNSIGNED NOT NULL,`clock` date NOT NULL,`BASE_diff` int(11) NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;")
cr.execute("CREATE TABLE `stream` (`id` int(20) UNSIGNED NOT NULL,`clock` datetime NOT NULL,`BASE` int(11) NOT NULL,`PAPP` int(11) NOT NULL,`BASE_diff` int(11) NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;")
cr.execute("ALTER TABLE `dailies` ADD PRIMARY KEY (`id`), ADD KEY `clock` (`clock`);")
cr.execute("ALTER TABLE `stream` ADD PRIMARY KEY (`id`), ADD KEY `clock` (`clock`);")
cr.execute("ALTER TABLE `dailies` MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT;")
cr.execute("ALTER TABLE `stream` MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT;")
db.commit()
except MySQLdb._exceptions.OperationalError:
log.critical('Something went wrong while trying to create database schema:', exc_info=True)
print('Something went wrong while trying to create database schema. See logs for more info.', file=sys.stderr)
Expand Down Expand Up @@ -110,18 +115,22 @@ def close_db(db):
db.close()


def insert_stream(db, cr, BASE, PAPP):
def insert_stream(config, db, cr, BASE, PAPP):
"""
Insert a record in the stream table
Args:
config (dict): Loaded config from yaml file
db (type): MySQLdb database object
cr (type): MySQLdb cursor object
BASE (int): Linky BASE value (Wh meter)
PAPP (int): Linky PAPP value (current VA power)
"""
# generating time
now = datetime.datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')
if config.get('use_utc', False):
now = datetime.datetime.now(datetime.timezone.utc).strftime('%Y-%m-%d %H:%M:%S')
else:
now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')

# retrieving previous BASE and calculating BASE_diff
cr.execute("SELECT BASE FROM stream ORDER BY clock DESC LIMIT 1;")
Expand All @@ -134,15 +143,16 @@ def insert_stream(db, cr, BASE, PAPP):
BASE_diff = BASE-int(previous)

# inserting records
cr.execute(f"INSERT INTO stream VALUES ('{now}', {BASE}, {PAPP}, {BASE_diff});")
cr.execute(f'INSERT INTO stream VALUES (NULL, %(now)s, %(BASE)s, %(PAPP)s, %(BASE_diff)s);', {"now": now, "BASE": BASE, "PAPP": PAPP, "BASE_diff": BASE_diff})
db.commit()


def insert_dailies(db, cr, BASE):
def insert_dailies(config, db, cr, BASE):
"""
Inserts a record in the dailies table
Args:
config (dict): Loaded config from yaml file
db (type): MySQLdb database object
cr (type): MySQLdb cursor object
BASE (int): Linky BASE value (Wh meter)
Expand All @@ -157,9 +167,12 @@ def insert_dailies(db, cr, BASE):
else:
diff = BASE-previous

now = datetime.datetime.utcnow().strftime('%Y-%m-%d')
if config.get('use_utc', False):
now = datetime.datetime.now(datetime.timezone.utc).strftime('%Y-%m-%d')
else:
now = datetime.datetime.now().strftime('%Y-%m-%d')

cr.execute(f'INSERT INTO dailies VALUES ("{now}", "{diff}")')
cr.execute(f'INSERT INTO dailies VALUES (NULL, %(now)s, %(diff)s)', {"now": now, "diff": diff})
db.commit()


Expand Down
26 changes: 19 additions & 7 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
config = linky.load_config()
log.debug(f'Config loaded! Values: {config}')

terminal = linky.setup_serial(config['device']['file'])
terminal = linky.setup_serial(config['device'])

# Trying to connect to db server and creating schema if not exists
linky.test_db_connection(config['database']['server'], config['database']['user'], config['database']['password'], config['database']['name'])
Expand All @@ -27,14 +27,22 @@
# Main loop #
# ----------------------------- #

current_loop_day = datetime.datetime.now(datetime.timezone.utc).day
previous_loop_day = datetime.datetime.now(datetime.timezone.utc).day
if config.get('use_utc', False):
current_loop_day = datetime.datetime.now(datetime.timezone.utc).day
previous_loop_day = datetime.datetime.now(datetime.timezone.utc).day
else:
current_loop_day = datetime.datetime.now().day
previous_loop_day = datetime.datetime.now().day

while True:
log.debug("Cycle begins")
data_BASE = None
data_PAPP = None
current_loop_day = datetime.datetime.now(datetime.timezone.utc).day

if config.get('use_utc', False):
current_loop_day = datetime.datetime.now(datetime.timezone.utc).day
else:
current_loop_day = datetime.datetime.now().day

# Now beginning to read data from Linky
log.debug("Opening terminal...")
Expand Down Expand Up @@ -63,12 +71,16 @@
# first record of the day? generating dailies
if current_loop_day != previous_loop_day:
log.debug(f"First record of the day! Inserting dailies record.")
linky.insert_dailies(db, cr, data_BASE)
previous_loop_day = datetime.datetime.utcnow().day
linky.insert_dailies(config, db, cr, data_BASE)

if config.get('use_utc', False):
previous_loop_day = datetime.datetime.now(datetime.timezone.utc).day
else:
previous_loop_day = datetime.datetime.now().day

# inserting values
log.debug("Inserting stream record")
linky.insert_stream(db, cr, data_BASE, data_PAPP)
linky.insert_stream(config, db, cr, data_BASE, data_PAPP)

log.debug("Cycle ends, sleeping for 60 seconds")
time.sleep(60)

0 comments on commit fd81723

Please sign in to comment.