Skip to content
This repository was archived by the owner on Jun 27, 2022. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
*~
*.pyc
home-assistant-core
home-assistant-core
.venv
63 changes: 47 additions & 16 deletions homeassistant2influxdb.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
#!/usr/bin/python3
#!.venv/bin/python3
# -*- coding: utf-8 -*-

import argparse
import json
import yaml

# MySQL / MariaDB
from MySQLdb import connect as mysql_connect, cursors
try:
from MySQLdb import connect as mysql_connect, cursors
except:
print("Warning: Could not load Mysql driver, might not be a problem if you intend to use sqlite")

# SQLite (not tested)
#import sqlite3
try:
import sqlite3
except:
print("Warning: Could not load sqlite3 driver, might not be a problem if you intend to use Mysql")


# progress bar
from tqdm import tqdm
Expand All @@ -22,6 +29,7 @@
sys.path.append("home-assistant-core")
from homeassistant.helpers import location
from homeassistant.core import Event, State
from homeassistant.util import dt
from homeassistant.components.influxdb import get_influx_connection, _generate_event_to_json, INFLUX_SCHEMA
from homeassistant.exceptions import InvalidEntityFormatError

Expand Down Expand Up @@ -63,25 +71,38 @@ def main():
"""

parser = argparse.ArgumentParser()
parser.add_argument('--type', '-t',
dest='type', action='store', required=True,
help='Database type: MySQL, MariaDB or SQLite')
parser.add_argument('--user', '-u',
dest='user', action='store', required=True,
dest='user', action='store', required=False,
help='MySQL/MariaDB username')
parser.add_argument('--password', "-p",
dest='password', action='store',
dest='password', action='store', required=False,
help='MySQL/MariaDB password')
parser.add_argument('--host', '-s',
dest='host', action='store', required=True,
dest='host', action='store', required=False,
help='MySQL/MariaDB host')
parser.add_argument('--database', '-d',
dest='database', action='store', required=True,
help='MySQL/MariaDB database')
help='MySQL/MariaDB database or SQLite databasefile')
parser.add_argument('--count', '-c',
dest='row_count', action='store', required=False, type=int, default=0,
help='If 0 (default), determine upper bound of number of rows by querying database, '
'otherwise use this number (used for progress bar only)')
parser.add_argument('--table', '-x',
dest='table', action='store', required=True,
help='Source Table is either states or statistics'
'Home Assistant keeps 10 days of states by default and keeps statistics forever for some entities')
parser.add_argument('--dry-run', '-y',
dest='dry', action='store_true', required=False,
help='do all work except writing to InfluxDB')

args = parser.parse_args()

if (args.dry) :
print("option --dry-run was given, nothing will be writen on InfluxDB")

# load InfluxDB configuration file (the one from Home Assistant) (without using !secrets)
with open("influxdb.yaml") as config_file:
influx_config = yaml.load(config_file, Loader=yaml.FullLoader)
Expand All @@ -94,12 +115,14 @@ def main():
influx = get_influx_connection(influx_config, test_write=True, test_read=True)
converter = _generate_event_to_json(influx_config)

# connect to MySQL/MariaDB database
connection = mysql_connect(host=args.host, user=args.user, password=args.password, database=args.database, cursorclass=cursors.SSCursor, charset="utf8")
cursor = connection.cursor()
if (args.type == "MySQL" or args.type == "MariaDB"):
# connect to MySQL/MariaDB database
connection = mysql_connect(host=args.host, user=args.user, password=args.password, database=args.database, cursorclass=cursors.SSCursor, charset="utf8")
else:
# connect to SQLite file instead
connection = sqlite3.connect(args.database)

# untested: connect to SQLite file instead (you need to get rid of the first three `add_argument` calls above)
#connection = sqlite3.connect('home_assistant_v2.db')
cursor = connection.cursor()

if args.row_count == 0:
# query number of rows in states table - this will be more than the number of rows we
Expand All @@ -110,7 +133,13 @@ def main():
total = args.row_count

# select the values we are interested in
cursor.execute("select states.entity_id, states.state, states.attributes, events.event_type, events.time_fired from states, events where events.event_id = states.event_id")
if (args.table == "states"):
cursor.execute("select states.entity_id, states.state, state_attributes.shared_attrs, events.event_type, events.time_fired from states, events, state_attributes where events.event_id = states.event_id and states.attributes_id=state_attributes.attributes_id")
elif (args.table == "statistics"):
cursor.execute("select statistic_id,mean,state_attributes.shared_attrs,'state_changed',created from statistics_meta,statistics,state_attributes where metadata_id= statistics_meta.id and mean!=0 and state_attributes.attributes_id = (select states.attributes_id from states where states.entity_id = statistic_id limit 1 )")
else:
print("ERROR: argument --table should be \"states\" or \"statistics\"");
exit

# map to count names and number of measurements for each entity
statistics = {}
Expand All @@ -129,7 +158,7 @@ def main():
_attributes_raw = row[2]
_attributes = rename_friendly_name(json.loads(_attributes_raw))
_event_type = row[3]
_time_fired = row[4]
_time_fired = dt.parse_datetime(row[4])
except Exception as e:
print("Failed extracting data from %s: %s.\nAttributes: %s" % (row, e, _attributes_raw))
continue
Expand Down Expand Up @@ -169,11 +198,13 @@ def main():
batch_size_cur += 1

if batch_size_cur >= batch_size_max:
influx.write(batch_json)
if (not args.dry) :
influx.write(batch_json)
batch_json = []
batch_size_cur = 0

influx.write(batch_json)
if (not args.dry) :
influx.write(batch_json)
influx.close()

# print statistics - ideally you have one friendly name per entity_id
Expand Down