{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## Datebase query examples\n", "This notebooks shows the two ways to access objects stored in your Home Assistant database." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Python's SQLite3 support\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This access method is very simple and perfect if you want to have a quick look at your data." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import sqlite3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Create a connection to the database." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "conn = sqlite3.connect('/path/to/.homeassistant/home-assistant_v2.db')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With a standard SQL query you will get access to the data. " ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "data = conn.execute(\"SELECT state, last_changed FROM states WHERE entity_id = 'sensor.random_sensor' AND state != 'unknown'\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now loop over the returned data." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "('5', '2018-12-29 22:35:23.002962')\n", "('10', '2018-12-29 22:35:54.004050')\n", "('11', '2018-12-29 22:36:25.003492')\n", "('9', '2018-12-29 22:36:56.002128')\n", "('10', '2018-12-29 22:37:26.003112')\n", "('3', '2018-12-29 22:37:57.003212')\n", "('7', '2018-12-29 22:38:28.003530')\n", "('0', '2018-12-29 22:40:24.706704')\n", "('13', '2018-12-29 22:40:55.003484')\n", "('2', '2018-12-29 22:41:26.002753')\n", "('16', '2018-12-29 22:41:57.002935')\n", "('0', '2018-12-29 22:42:28.002090')\n", "('13', '2018-12-29 22:42:58.003726')\n", "('15', '2018-12-29 22:43:29.003645')\n", "('10', '2018-12-29 22:44:00.003698')\n", "('16', '2018-12-29 22:44:31.005287')\n", "('9', '2018-12-29 22:45:02.003470')\n", "('4', '2018-12-29 22:46:46.708584')\n", "('18', '2018-12-29 22:47:17.004101')\n", "('13', '2018-12-29 22:47:48.002298')\n", "('6', '2018-12-29 22:48:18.002954')\n", "('2', '2018-12-29 22:48:38.645046')\n", "('0', '2018-12-29 22:49:09.002633')\n", "('15', '2018-12-29 22:49:39.004924')\n", "('4', '2018-12-29 22:50:10.004746')\n", "('1', '2018-12-29 22:50:27.419122')\n" ] } ], "source": [ "for row in data:\n", " print(row)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### `sqlalchemy`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[SQLAlchemy](https://www.sqlalchemy.org/) is a SQL toolkit and object-relational mapper. " ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false, "scrolled": true }, "outputs": [], "source": [ "from sqlalchemy import create_engine\n", "from sqlalchemy.orm import scoped_session, sessionmaker\n", "\n", "from homeassistant.components.recorder.models import Base, Events, States, RecorderRuns" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": true, "scrolled": true }, "outputs": [], "source": [ "DB_URL = \"sqlite:////path/to/.homeassistant/home-assistant_v2.db\"" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Database setup\n", "engine = create_engine(DB_URL)\n", "Base.metadata.create_all(engine)\n", "session_factory = sessionmaker(bind=engine)\n", "Session = scoped_session(session_factory)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Available entities and their total number of unique states" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": false, "scrolled": false }, "outputs": [ { "data": { "text/plain": [ "[('5', '2018-12-29 22:35:23.002962'),\n", " ('10', '2018-12-29 22:35:54.004050'),\n", " ('11', '2018-12-29 22:36:25.003492'),\n", " ('9', '2018-12-29 22:36:56.002128'),\n", " ('10', '2018-12-29 22:37:26.003112'),\n", " ('3', '2018-12-29 22:37:57.003212'),\n", " ('7', '2018-12-29 22:38:28.003530'),\n", " ('0', '2018-12-29 22:40:24.706704'),\n", " ('13', '2018-12-29 22:40:55.003484'),\n", " ('2', '2018-12-29 22:41:26.002753'),\n", " ('16', '2018-12-29 22:41:57.002935'),\n", " ('0', '2018-12-29 22:42:28.002090'),\n", " ('13', '2018-12-29 22:42:58.003726'),\n", " ('15', '2018-12-29 22:43:29.003645'),\n", " ('10', '2018-12-29 22:44:00.003698'),\n", " ('16', '2018-12-29 22:44:31.005287'),\n", " ('9', '2018-12-29 22:45:02.003470'),\n", " ('4', '2018-12-29 22:46:46.708584'),\n", " ('18', '2018-12-29 22:47:17.004101'),\n", " ('13', '2018-12-29 22:47:48.002298'),\n", " ('6', '2018-12-29 22:48:18.002954'),\n", " ('2', '2018-12-29 22:48:38.645046'),\n", " ('0', '2018-12-29 22:49:09.002633'),\n", " ('15', '2018-12-29 22:49:39.004924'),\n", " ('4', '2018-12-29 22:50:10.004746'),\n", " ('1', '2018-12-29 22:50:27.419122')]" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(engine.execute(\"SELECT state, last_changed FROM states WHERE entity_id = 'sensor.random_sensor' AND state != 'unknown'\"))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Get available runs" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false, "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "['2018-12-29 22:35:21.556017',\n", " '2018-12-29 22:40:23.336551',\n", " '2018-12-29 22:46:45.356893',\n", " '2018-12-29 22:48:37.321686',\n", " '2018-12-29 22:50:26.139649']" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[str(row.start) for row in Session().query(RecorderRuns)]" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": true, "scrolled": true }, "outputs": [], "source": [ "# Last finished run\n", "last_run = (Session().query(RecorderRuns)\n", " .filter(RecorderRuns.end != None)\n", " .order_by(RecorderRuns.end.desc())\n", " .first().to_native())" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2018-12-29 22:50:56.243402\n" ] } ], "source": [ "print(last_run.end)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Show states at point in time" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": false, "scrolled": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Demo Air Pollutants Home : 14 \n", "Demo Air Pollutants Office : 4 \n", "Heizung Wohnzimmer AUS : on \n", "Heizung Wohnzimmer EIN : on \n", "Random Binary Sensor : on \n", "Bärenplatz : idle \n", "Camera Proxy - camera.barenplatz : idle \n", "all automations : on \n", "Random Sensor : 1 \n", "Sun : below_horizon \n" ] } ], "source": [ "from datetime import timedelta, datetime\n", "from sqlalchemy import and_, func\n", "\n", "point_in_time = datetime(2018, 12, 29, 23, 40, 0)\n", "\n", "# Taken from homeassistant.components.history#get_states\n", "most_recent_state_ids = (\n", " Session().query(func.max(States.state_id).label('max_state_id'))\n", " .filter(States.created < point_in_time).group_by(States.entity_id).subquery()\n", ")\n", "\n", "query = Session().query(States).join(\n", " most_recent_state_ids, and_(\n", " States.state_id == most_recent_state_ids.c.max_state_id))\n", "\n", "for state in query:\n", " # Convert to a Home Assistant object\n", " state = state.to_native()\n", " print(state.name, \":\", state.state, state.attributes.get('unit_of_measurement', ''))" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.1" } }, "nbformat": 4, "nbformat_minor": 2 }