diff --git a/source/_components/binary_sensor.knx.markdown b/source/_components/binary_sensor.knx.markdown index f9af2538a4a..ff99d54be20 100644 --- a/source/_components/binary_sensor.knx.markdown +++ b/source/_components/binary_sensor.knx.markdown @@ -7,7 +7,7 @@ sidebar: true comments: false sharing: true footer: true -logo: zknx.png +logo: knx.png ha_category: Binary Sensor ha_release: 0.24 --- diff --git a/source/_components/garage_door.rpi_gpio.markdown b/source/_components/garage_door.rpi_gpio.markdown index bb5a83000bc..ecdae40cb0a 100644 --- a/source/_components/garage_door.rpi_gpio.markdown +++ b/source/_components/garage_door.rpi_gpio.markdown @@ -7,11 +7,21 @@ sidebar: true comments: false sharing: true footer: true -logo: wink.png +logo: raspberry-pi.png ha_category: Garage Door ha_release: 0.23 --- +The `rpi_gpio` garage door platform allows you to use a Raspberry Pi to control your Garage door. + +It uses two pins on the Raspberry Pi. +- The `state_pin` will detect if the door is closed, and +- the `relay_pin` will trigger the door to open or close. + +Although you do not need Andrews Hilliday's software controller when you run Home Assistant, he has written clear instructions on how to hook your garage door & sensors up to your Raspberry Pi, which can be found [here](https://github.com/andrewshilliday/garage-door-controller#hardware-setup). + +To enable Raspberry Pi Garage doors in your installation, add the following to your `configuration.yaml` file: + ```yaml # Example configuration.yaml entry garage_door: @@ -23,4 +33,12 @@ garage_door: - relay_pin: 12 state_pin: 13 name: 'Right door' -``` \ No newline at end of file +``` + +Configuration variables: + +- **doors** array (*Required*): List of your doors. + - **name** (*Optional*): Name to use in the Frontend. + - **relay_pin** (*Required*): The pin of your Raspberry Pi where the relay is connected. + - **state_pin** (*Required*): The pin of your Raspberry Pi to retrieve the state. + diff --git a/source/_components/group.markdown b/source/_components/group.markdown index 3eb481b314f..c26b435e6e8 100644 --- a/source/_components/group.markdown +++ b/source/_components/group.markdown @@ -64,11 +64,13 @@ Notice in the example below that in order to refer to the group "Living Room", y entities: - light.light_family_1 - binary_sensor.motion_living + Bedroom: light.light_bedroom, switch.sleeping - Rooms: - view: yes + + Rooms: + view: yes name: Rooms - entities: - - group.living_room + entities: + - group.living_room - group.bedroom -``` \ No newline at end of file +``` diff --git a/source/_components/light.rfxtrx.markdown b/source/_components/light.rfxtrx.markdown index 5d646d0fa0e..12bdc7cfd06 100644 --- a/source/_components/light.rfxtrx.markdown +++ b/source/_components/light.rfxtrx.markdown @@ -58,5 +58,5 @@ Configuration variables: - **devices** (*Required*): A list of devices with their name to use in the frontend. - **automatic_add** (*Optional*): To enable the automatic addition of new lights. -- **signal_repetitions** *Optional*: Because the rxftrx device sends its actions via radio and from most receivers it's impossible to know if the signal was received or not. Therefore you can configure the switch to try to send each signal repeatedly. -- **fire_event** *Optional*: Fires an event even if the state is the same as before. Can be used for automations. +- **signal_repetitions** (*Optional*): Because the rxftrx device sends its actions via radio and from most receivers it's impossible to know if the signal was received or not. Therefore you can configure the switch to try to send each signal repeatedly. +- **fire_event** (*Optional*): Fires an event even if the state is the same as before. Can be used for automations. diff --git a/source/_components/media_player.braviatv.markdown b/source/_components/media_player.braviatv.markdown index 06c19b6d997..affcdff7b22 100644 --- a/source/_components/media_player.braviatv.markdown +++ b/source/_components/media_player.braviatv.markdown @@ -33,3 +33,8 @@ Configuration variables: - **host** (*Required*): The IP of the Sony Bravia TV, eg. 192.168.0.10 - **name** (*Optional*): The name to use on the frontend. +You are also able to configure the TV manually by placing a `bravia.conf` file in your `.homeassistant` config directory with the following information - please update the details to match your setup: + +```json +{"192.168.0.10": {"pin": "7745", "mac": "ac:1e:0a:e1:0c:01"}} +``` diff --git a/source/_components/media_player.plex.markdown b/source/_components/media_player.plex.markdown index a90abe77d89..7f462f1191c 100644 --- a/source/_components/media_player.plex.markdown +++ b/source/_components/media_player.plex.markdown @@ -16,7 +16,17 @@ ha_release: 0.7.4 The `plex` platform allows you to connect a [Plex Media Server](https://plex.tv) to Home Assistant. It will allow you to control media playback and see the current playing item. The preferred way to setup the Plex platform is by enabling the the [the discovery component](/components/discovery/) and requires GDM to be enabled. -If local authentication is enabled or multiple users are defined, HASS requires an authentication token to be entered in the webinterface. See [Finding your account token / X-Plex-Token](https://support.plex.tv/hc/en-us/articles/204059436). +If local authentication is enabled or multiple users are defined, Home Assistant requires an authentication token to be entered in the frontend. Press "CONFIGURE" to do it. + +

+ +

+ +If you don't know your token, see [Finding your account token / X-Plex-Token](https://support.plex.tv/hc/en-us/articles/204059436). + +

+ +

If you want to enable the plex platform directly, add the following lines to your `configuration.yaml`: @@ -26,7 +36,7 @@ media_player: platform: plex ``` -In case discovery does not work (GDM disabled or non-local plex server), you can create `~/.homeassistant/plex.conf` manually. +In case [discovery](/components/discovery/) does not work (GDM disabled or non-local plex server), you can create `~/.homeassistant/plex.conf` manually. ```json {"IP_ADDRESS:PORT": {"token": "TOKEN"}} diff --git a/source/_components/media_player.samsungtv.markdown b/source/_components/media_player.samsungtv.markdown index c6eef02d513..10f179e0328 100644 --- a/source/_components/media_player.samsungtv.markdown +++ b/source/_components/media_player.samsungtv.markdown @@ -40,6 +40,7 @@ Currently known supported models: - D8000 - ES5500 - ES6800 +- F6300 - F6500 - EH5600 - F6400AF diff --git a/source/_components/notify.telegram.markdown b/source/_components/notify.telegram.markdown index ee90fb0297a..c9b621625e9 100644 --- a/source/_components/notify.telegram.markdown +++ b/source/_components/notify.telegram.markdown @@ -60,18 +60,15 @@ notify: Configuration variables: -- **name** (*Optional*): Setting the optional parameter `name` allows multiple notifiers to be created. The default value is `notify`. The notifier will bind to the service -`notify.NOTIFIER_NAME`. +- **name** (*Optional*): Setting the optional parameter `name` allows multiple notifiers to be created. The default value is `notify`. The notifier will bind to the service `notify.NOTIFIER_NAME`. - **api_key** (*Required*): The API token of your bot. - **chat_id** (*Required*: The chat ID of your user. To use notifications, please see the [getting started with automation page](/getting-started/automation/). -### Photo support +### {% linkable_title Photo support %} ```yaml -... - action: service: notify.NOTIFIER_NAME data: @@ -88,6 +85,8 @@ action: caption: I.e. for a Title ``` -- **url** or **file** (*Required*): For local or remote path to a picture -- **caption** (*Optional*: Picture title -- **username** and **password** (*Optional*: For URL they require a basic auth +- **url** or **file** (*Required*): For local or remote path to an image. +- **caption** (*Optional*): The title of the image. +- **username** (*Optional*): Username for an URL which require HTTP basic authentication. +- **password** (*Optional*): Username for an URL which require HTTP basic authentication. + diff --git a/source/_components/recorder.markdown b/source/_components/recorder.markdown index 989f984a360..ad7a5c7902e 100644 --- a/source/_components/recorder.markdown +++ b/source/_components/recorder.markdown @@ -41,3 +41,16 @@ Configuration variables: | PostgreSQL | `postgresql://SERVER_IP/DB_NAME` | | PostgreSQL | `postgresql://scott:tiger@SERVER_IP/DB_NAME` | +## {% linkable_title Installation notes %} + +Not all Python bindings for the choosen database engine can be installed directly. This section contains additional details which should help you to get it working. + +### {% linkable_title MYSQL %} + +For MySQL you may have to install a few dependencies: + +```bash +$ sudo apt-get install libmysqlclient-dev +$ pip3 install mysqlclient +``` + diff --git a/source/_components/rollershutter.rfxtrx.markdown b/source/_components/rollershutter.rfxtrx.markdown index ffe8f6fd98e..daa43ef8b04 100644 --- a/source/_components/rollershutter.rfxtrx.markdown +++ b/source/_components/rollershutter.rfxtrx.markdown @@ -63,5 +63,5 @@ Configuration variables: - **devices** (*Required*): A list of devices with their name to use in the frontend. - **automatic_add** (*Optional*): To enable the automatic addition of new roller shutters (Siemens/LightwaveRF only). -- **signal_repetitions** *Optional*: Because the rxftrx device sends its actions via radio and from most receivers it's impossible to know if the signal was received or not. Therefore you can configure the roller shutter to try to send each signal repeatedly. -- **fire_event** *Optional*: Fires an event even if the state is the same as before. Can be used for automations. +- **signal_repetitions** (*Optional*): Because the rxftrx device sends its actions via radio and from most receivers it's impossible to know if the signal was received or not. Therefore you can configure the roller shutter to try to send each signal repeatedly. +- **fire_event** (*Optional*): Fires an event even if the state is the same as before. Can be used for automations. diff --git a/source/_components/sensor.google_travel_time.markdown b/source/_components/sensor.google_travel_time.markdown index 01a1643484d..ad0d343c725 100644 --- a/source/_components/sensor.google_travel_time.markdown +++ b/source/_components/sensor.google_travel_time.markdown @@ -34,16 +34,15 @@ sensor: Configuration variables: - **api_key** (*Required*): Your application's API key (get one by following the instructions above). This key identifies your application for purposes of quota management. -- **origin** (*Required*): The starting point for calculating travel distance and time. You can supply one or more locations separated by the pipe character, in the form of an address, latitude/longitude coordinates, or a Google place ID. +- **origin** (*Required*): The starting point for calculating travel distance and time. You can supply one or more locations separated by the pipe character, in the form of an address, latitude/longitude coordinates, or a [Google place ID](https://developers.google.com/places/place-id). When specifying the location using a Google place ID, the ID must be prefixed with `place_id:`. - **destination** (*Required*): One or more locations to use as the finishing point for calculating travel distance and time. The options for the destinations parameter are the same as for the origins parameter, described above. - **name** (*Optional*): A name to display on the sensor. The default is "Google Travel Time - " where transit mode is the mode set in options for the sensor (if no mode is set, the default is driving). - **options** (*Optional*): A dictionary containing parameters to add to all requests to the Distance Matrix API. A full listing of available options can be found [here](https://developers.google.com/maps/documentation/distance-matrix/intro#RequestParameters). - **departure_time** (*Optional*): Can be `now`, a Unix timestamp, or a 24 hour time string like `08:00:00`. If you provide a time string, it will be combined with the current date to get travel time for that moment. - **arrival_time** (*Optional*): See notes above for `departure_time`. `arrival_time` can not be `now`, only a Unix timestamp or time string. You can not provide both `departure_time` and `arrival_time`. If you do provide both, `arrival_time` will be removed from the request. +##### {% linkable_title Dynamic Configuration %} - -###Dynamic Configuration Tracking can be setup to track entities of type device_tracker, zone, and sensor. If an entity is placed in the origin or destination then every 5 minutes when the component updates it will use the latest location of that entity. ```yaml @@ -64,7 +63,8 @@ sensor: destination: Eddies House # Friendly name of a zone ``` -####Entity Tracking +#### {% linkable_title Entity Tracking %} + - **device_tracker** - If state is a zone then the zone location will be used - If state is not a zone it will look for the longitude and latitude attributes diff --git a/source/_components/sensor.rfxtrx.markdown b/source/_components/sensor.rfxtrx.markdown index d48a5c98a54..5a59df62e6b 100644 --- a/source/_components/sensor.rfxtrx.markdown +++ b/source/_components/sensor.rfxtrx.markdown @@ -74,4 +74,4 @@ Configuration variables: - **devices** (*Optional*): A list of devices with their name to use in the frontend. - **automatic_add** (*Optional*): To enable the automatic addition of new lights. - **data_type** (*Optional*): Which data type the sensor should show -- **fire_event** *Optional*: Fires an event even if the state is the same as before. Can be used for automations. +- **fire_event** (*Optional*): Fires an event even if the state is the same as before. Can be used for automations. diff --git a/source/_components/sensor.template.markdown b/source/_components/sensor.template.markdown index d5de67dace4..70f56d15e56 100644 --- a/source/_components/sensor.template.markdown +++ b/source/_components/sensor.template.markdown @@ -58,7 +58,18 @@ sensor: ### {% linkable_title Renaming sensor output %} -If you don't like the wording of a sensor output then the template sensor can help too. Processes monitored by the [System Monitor sensor](/components/sensor.systemmonitor/) show `on` or `off` if they are running or not. This example shows how the output of a monitored `glances` process can be renamed. +If you don't like the wording of a sensor output then the template sensor can help too. Let's rename the output of the [Sun component](/components/sun/) as a simple example: + +```yaml +sensor: + platform: template + sensors: + sun_state: + value_template: {% raw %}'{% if is_state("sun.sun", "above_horizon") %}up{% else %}down{% endif %}'{% endraw %} + friendly_name: 'Sun state' +``` + +Processes monitored by the [System Monitor sensor](/components/sensor.systemmonitor/) show `on` or `off` if they are running or not. This example shows how the output of a monitored `glances` process can be renamed. ```yaml sensor: diff --git a/source/_components/sensor.yweather.markdown b/source/_components/sensor.yweather.markdown index 5a070f4e76d..0c9c1ccbd29 100644 --- a/source/_components/sensor.yweather.markdown +++ b/source/_components/sensor.yweather.markdown @@ -13,13 +13,13 @@ ha_release: 0.24 --- -The `yweather` platform uses [Yahoo Weather](http://https://www.yahoo.com/news/weather/) as an source for current meteorological data. The `forecast` will show you the condition for 5 days, 0 is the current day. You can use only `weather`, `temp_min`, and `temp_max` with forecast. +The `yweather` platform uses [Yahoo Weather](https://www.yahoo.com/news/weather/) as an source for current meteorological data. The `forecast` will show you the condition for 5 days, 0 is the current day. You can use only `weather`, `temp_min`, and `temp_max` with forecast.

Use of the Yahoo Weather API should not exceed reasonable request volume. Access is limited to 2,000 signed calls per day.

-The `woeid` (Where On Earth ID) for your location, as shown in the example below. You can find your woeid by copying the numeric digits at the end of the URL for your location at [Yahoo Weather](http://https://www.yahoo.com/news/weather/). If you don't add a woeid it generate it from Home Assistant's latitude and longitude. +The `woeid` (Where On Earth ID) for your location, as shown in the example below. You can find your woeid by copying the numeric digits at the end of the URL for your location at [Yahoo Weather](https://www.yahoo.com/news/weather/). If you don't add a woeid it generate it from Home Assistant's latitude and longitude. To add Yahoo Weather to your installation, add the following to your `configuration.yaml` file: @@ -66,4 +66,6 @@ Configuration variables: - **pressure**: The sea-level air pressure in millibars. - **visibility**: The average visibility. -Details about the API are available in the [Yahoo! EDeveloper Network](http://https://developer.yahoo.com/weather/). + +Details about the API are available in the [Yahoo! Developer Network](https://developer.yahoo.com/weather/). + diff --git a/source/_components/switch.hikvision.markdown b/source/_components/switch.hikvision.markdown index 36f4581e40e..6434dcc5f46 100644 --- a/source/_components/switch.hikvision.markdown +++ b/source/_components/switch.hikvision.markdown @@ -26,6 +26,7 @@ To use your Hikvision cam in your installation, add the following to your `confi switch: platform: hikvisioncam host: 192.168.1.32 + port: 80 name: Hikvision Cam 1 Motion Detection username: USERNAME password: PASSWORD @@ -34,6 +35,7 @@ switch: Configuration variables: - **host** *Required*: The IP address of your Hikvision camera, eg. 192.168.1.32 +- **port** *Optional*: The port to connec to your Hikvision camera (default is 80). - **name** *Optional*: This parameter allows you to override the name of your camera. - **username** *Required*: The username for accessing your Hikvision camera. - **password** *Required*: The password to access your Hikvision camera. diff --git a/source/_components/switch.knx.markdown b/source/_components/switch.knx.markdown index d90f21ccb06..470d241d5fa 100644 --- a/source/_components/switch.knx.markdown +++ b/source/_components/switch.knx.markdown @@ -7,7 +7,11 @@ sidebar: true comments: false sharing: true footer: true +<<<<<<< HEAD logo: zknx.png +======= +logo: knx.png +>>>>>>> master ha_category: Switch ha_release: 0.24 --- diff --git a/source/_components/switch.rfxtrx.markdown b/source/_components/switch.rfxtrx.markdown index bb4aa83c503..b5cfb786b6d 100644 --- a/source/_components/switch.rfxtrx.markdown +++ b/source/_components/switch.rfxtrx.markdown @@ -63,5 +63,5 @@ Configuration variables: - **devices** (*Required*): A list of devices with their name to use in the frontend. - **automatic_add** (*Optional*): To enable the automatic addition of new switches. -- **signal_repetitions** *Optional*: Because the rxftrx device sends its actions via radio and from most receivers it's impossible to know if the signal was received or not. Therefore you can configure the switch to try to send each signal repeatedly. -- **fire_event** *Optional*: Fires an event even if the state is the same as before. Can be used for automations. +- **signal_repetitions** (*Optional*): Because the rxftrx device sends its actions via radio and from most receivers it's impossible to know if the signal was received or not. Therefore you can configure the switch to try to send each signal repeatedly. +- **fire_event** (*Optional*): Fires an event even if the state is the same as before. Can be used for automations. diff --git a/source/_components/thermostat.radiotherm.markdown b/source/_components/thermostat.radiotherm.markdown index fc409933030..c5845e09793 100644 --- a/source/_components/thermostat.radiotherm.markdown +++ b/source/_components/thermostat.radiotherm.markdown @@ -34,7 +34,7 @@ thermostat: Configuration variables: - **host** (*Required*): List of your Radiotherm thermostats -- **hold_temp** (*Required*): Boolean to control if hass temp adjustments hold(True) or are temporary(False) +- **hold_temp** (*Required*): Boolean to control if Home Assistant temperature adjustments hold (`True`) or are temporary (`False`). Temperature settings from Home Assistant will be sent to thermostat and then hold at that temperature. Set to `False` if you set a thermostat schedule on the thermostat itself and just want Home Assistant to send temporary temperature changes. diff --git a/source/_cookbook/automation_sun.markdown b/source/_cookbook/automation_sun.markdown index 02b1e100b54..dd82edb8519 100644 --- a/source/_cookbook/automation_sun.markdown +++ b/source/_cookbook/automation_sun.markdown @@ -46,7 +46,7 @@ automation: #### {% linkable_title Send sun rise/sun set notifications %} -Notifications send through [PushBullet](components/notify.pushbullet/) when the sun state is changed. +Notifications send through [PushBullet](/components/notify.pushbullet/) when the sun state is changed. ```yaml automation: diff --git a/source/_cookbook/notify_if__new_ha_release.markdown b/source/_cookbook/notify_if__new_ha_release.markdown new file mode 100644 index 00000000000..1c760a8b3f4 --- /dev/null +++ b/source/_cookbook/notify_if__new_ha_release.markdown @@ -0,0 +1,32 @@ +--- +layout: page +title: "Send notification if new Home Assistant release" +description: "Basic example of how to send a notification if a new Home Assistant release is available" +date: 2016-07-17 10:00 +sidebar: true +comments: false +sharing: true +footer: true +ha_category: Automation Examples +--- + +The following example sends a notification via XMPP if a new Home Assistant release is available: + +```yaml +notify: + - platform: xmpp + name: jabber + sender: sender@jabber.org + password: !secret xmpp_password + recipient: recipient@jabber.org + +automation: + - alias: Update notifications + trigger: + - platform: state + entity_id: updater.updater + action: + service: notify.jabber + data: + message: 'There is a new Home Assistant release available.' +``` diff --git a/source/_cookbook/track_battery_level.markdown b/source/_cookbook/track_battery_level.markdown index 905ae5f30fa..3c995af7d7c 100644 --- a/source/_cookbook/track_battery_level.markdown +++ b/source/_cookbook/track_battery_level.markdown @@ -30,6 +30,7 @@ The `else` part is used to have the sensor keep it's last state if the newest [i While running the [Owntracks](/components/device_tracker.owntracks/) device tracker you can retrieve the battery level with a MQTT sensor. ```yaml +sensor: - platform: mqtt state_topic: "owntracks/tablet/tablet" name: "Battery Tablet" diff --git a/source/_posts/2016-07-16-sqlalchemy-knx-join-simplisafe.markdown b/source/_posts/2016-07-16-sqlalchemy-knx-join-simplisafe.markdown index 166e2a226c4..8cc80cbbde5 100644 --- a/source/_posts/2016-07-16-sqlalchemy-knx-join-simplisafe.markdown +++ b/source/_posts/2016-07-16-sqlalchemy-knx-join-simplisafe.markdown @@ -47,16 +47,26 @@ You can omit the `--config` option if you use the default configuration director - Frontend: Fix stream not reconnecting after standby ([@balloob]) - Frontend: Wait up to two seconds for new state before resetting toggle after toggling state ([@balloob]) +<<<<<<< HEAD +======= +### {% linkable_title Hotfix 0.24.1 - July 21 %} + +Quick hot fix after we found a bug in the migrator where it wouldn't work with a database in a non-standard location. Thanks to [@n8henrie] and [@AlucardZero]. + +>>>>>>> master ### {% linkable_title Breaking changes %} - Migrating existing databases (see above). - The [APCUPSd Sensor][apcupsd-sensor] was updated. This will need that you modify your `configuration.yaml` file. - Entity IDs of Verisure locks will change. This is a one time change but should improve readability. +<<<<<<< HEAD ### {% linkable_title FAQ %} - Frequently asked questions about this release will show up here... +======= +>>>>>>> master [@bah2830]: https://github.com/bah2830/ [@balloob]: https://github.com/balloob/ [@dale3h]: https://github.com/dale3h/ @@ -70,6 +80,11 @@ You can omit the `--config` option if you use the default configuration director [@turbokongen]: https://github.com/turbokongen/ [@usul27]: https://github.com/usul27 [@w1ll1am23]: https://github.com/w1ll1am23/ +<<<<<<< HEAD +======= +[@n8henrie]: https://github.com/n8henrie/ +[@AlucardZero]: https://github.com/AlucardZero/ +>>>>>>> master [apcupsd-sensor]: /components/sensor.apcupsd/ [Brightness]: /components/light/ diff --git a/source/_posts/2016-07-19-visualizing-your-iot-data.markdown b/source/_posts/2016-07-19-visualizing-your-iot-data.markdown new file mode 100644 index 00000000000..a0f865a8cbc --- /dev/null +++ b/source/_posts/2016-07-19-visualizing-your-iot-data.markdown @@ -0,0 +1,114 @@ +--- +layout: post +title: "Visualize your IoT data" +description: "Export, process, and visualize your Home Assistant data." +date: 2016-07-19 16:00:00 +0000 +date_formatted: "July 19, 2016" +author: Fabian Affolter +author_twitter: fabaff +comments: true +categories: How-To IoT-Data +og_image: /images/blog/2016-07-reporting/libreoffice-graph.png +--- + + + +The [history component](/components/history/) is tracking everything that is going on within Home Assistant. This means that you have access to all stored information about your home. Our history is not a full-fledged graphical processing and visualization component as you may know from systems and network monitoring tools. The current limitation is that you only can select a day for a visual output of your information and not a period. Also, there is no possibility to drill down on a specific entity. + +This blog post will show you ways to export data for reporting, visualization, or further analysis of automation rules. + + + +In this blog post I use the temperature of the [Aare](https://en.wikipedia.org/wiki/Aare) river close to where I live as a show case. The temperatures were recorded with the [Swiss Hydrological Data sensor](/components/sensor.swiss_hydrological_data/) and the name of the sensor is `sensor.aare`. + +The database is stored at `/.homeassistant/home-assistant_v2.db` as [SQLite database](https://www.sqlite.org/). In all examples we are going to use the path: `/home/ha/.homeassistant/home-assistant_v2.db` + +If you are just curious what's stored in your database then you can use the `sqlite3` command-line tool or a graphical one like [DB Browser for SQLite](http://sqlitebrowser.org/). + +The table that is holding the states is called `states`. The `events` tables is responsible for storing the events which occurred. So, we will first check how many entries there are in the `states` table. `sqlite3` needs to know where the databases is located. To work with your database make sure that Home Assistant is not running or create a copy of the existing database. It's recommended to work with a copy. + +```bash +$ sqlite3 /home/ha/.homeassistant/home-assistant_v2.db +SQLite version 3.11.0 2016-02-15 17:29:24 +sqlite> SELECT count(*) FROM states; +24659 +``` + +Let's have a look at a sample [SQL](https://en.wikipedia.org/wiki/SQL) query. This query will show all states in a period for the sensor `sensor.aare`. + +```sql +SELECT state, last_changed FROM states + WHERE + entity_id = 'sensor.aare' + AND + last_changed BETWEEN + '2016-07-05 00:00:00.000000' AND '2016-07-07 00:00:00.000000'; +``` + +The SQL statement can be formed that it fits exactly what you need. This means that you can process the data in any way you want for further use. Often it makes sense to eliminate certain entries like `Unknown` or peaks. + +If the above query is executed in DB Browser for SQLite you would be able to save the sensor's graph as png. + +

+ + Visualization with DB Browser for SQLite +

+ +You may ask: Why not do this with LibreOffice Calc or another spreadsheet application? As most spreadsheet applications are not able to work directly with SQLite database we are going to export the data from the database to [CSV](https://en.wikipedia.org/wiki/Comma-separated_values). + +```bash +$ sqlite3 -header -csv /home/ha/.homeassistant/home-assistant_v2.db "SELECT last_changed, state FROM states WHERE entity_id = 'sensor.aare' AND last_changed BETWEEN '2016-07-05 00:00:00.000000' AND '2016-07-07 00:00:00.000000';" > sensor.csv +``` + +The ordering for the `SELECT` was changed to get the time stamps first and then the state. Now we can import the CSV file into the application of your choice, here it's LibreOffice Calc. + +

+ + Import of the CSV file +

+ +After the import a graph can be created over the existing data. + +

+ + Graph in LibreOffice +

+ +You can also use [matplotlib](http://matplotlib.org/) to generate graphs as an alternative to a spreadsheet application. This is a powerful Python 2D plotting library. With the built-in support for SQLite in Python it will only take a couple lines of code to visualize your data. + +```python +import sqlite3 +from matplotlib import dates +import matplotlib.pyplot as plt + +import homeassistant.util.dt as dt + +values = [] +timestamps = [] + +conn = sqlite3.connect('/home/ha/.homeassistant/home-assistant_v2.db') +data = conn.execute("SELECT state, last_changed FROM states WHERE " + "entity_id = 'sensor.aare' AND last_changed BETWEEN " + "'2016-07-05 00:00:00.000000' AND " + "'2016-07-07 00:00:00.000000'") + +for x in data: + timestamps.append(dates.date2num(dt.parse_datetime(x[1]))) + values.append(float(x[0])) + +plt.plot_date(x=timestamps, y=values, fmt="r-") +plt.ylabel('Temperature') +plt.xlabel('Time line') + +plt.savefig('sensor.png') +``` + +Creating a connection to the database and executing a query is similar to the ways already seen. The return values from the query are splitted into two lists. The time stamps must be converted in an value which is accepted by matplotlib and then the graph is generated and saved as image. + +

+ + Sensor graph generated by matplotlib +

+ +Most of the graphs are pretty ugly. So, further beautification will be needed. If you have created a nice report including some amazing graphs then the Home Assistant community would be grateful for sharing them in our [forum](https://community.home-assistant.io/). + diff --git a/source/_topics/splitting_configuration.markdown b/source/_topics/splitting_configuration.markdown index 7b613d780c5..91cb7b823bc 100644 --- a/source/_topics/splitting_configuration.markdown +++ b/source/_topics/splitting_configuration.markdown @@ -22,7 +22,7 @@ In this lighter version we will still need what could be called the core snippet ```yaml homeassistant: # Name of the location where Home Assistant is running - name: My Hass Instance + name: My Home Assistant Instance # Location required to calculate the time the sun rises and sets latitude: 37 longitude: -121 @@ -69,7 +69,7 @@ As with the core snippet, indentation makes a difference. The component headers While some of these components can technically be moved to a separate file they are so small or "one off's" where splitting them off is superfluous. Also, you'll notice the # symbol (hash/pound). This represents a "comment" as far as the commands are interpreted. Put another way, any line prefixed with a `#` will be ignored. This makes breaking up files for human readability really convenient, not to mention turning off features while leaving the entry intact. (Look at the `zigbee:` entry above and the b entry further down) -Now, lets assume that a blank file has been created in the hass configuration directory for each of the following: +Now, lets assume that a blank file has been created in the Home Assistant configuration directory for each of the following: ```text automation.yaml diff --git a/source/_topics/templating.markdown b/source/_topics/templating.markdown index d127a9b501d..6b2d6a75286 100644 --- a/source/_topics/templating.markdown +++ b/source/_topics/templating.markdown @@ -59,21 +59,21 @@ script: Home Assistant adds extensions to allow templates to access all of the current states: - - Iterating `states` will yield each state sorted alphabetically by entity ID. - - Iterating `states.domain` will yield each state of that domain sorted alphabetically by entity ID. - - `states.sensor.temperature` returns the state object for `sensor.temperature`. - - `states('device_tracker.paulus')` will return the state string (not the object) of the given entity or `unknown` if it doesn't exist. - - `is_state('device_tracker.paulus', 'home')` will test if the given entity is specified state. - - `is_state_attr('device_tracker.paulus', 'battery', 40)` will test if the given entity is specified state. - - Filter `multiply(x)` will convert the input to a number and multiply it with `x`. - - Filter `round(x)` will convert the input to a number and round it to `x` decimals. - - `now` will be rendered as current time in your time zone. - - `utcnow` will be rendered as UTC time. - - `as_timestamp` will convert datetime object or string to UNIX timestamp - - `distance()` will measure the distance in meters between home, entity, coordinates. - - `closest()` will find the closest entity. - - `relative_time(timestamp)` will format the date time as relative time vs now (ie 7 seconds) - +- Iterating `states` will yield each state sorted alphabetically by entity ID. +- Iterating `states.domain` will yield each state of that domain sorted alphabetically by entity ID. +- `states.sensor.temperature` returns the state object for `sensor.temperature`. +- `states('device_tracker.paulus')` will return the state string (not the object) of the given entity or `unknown` if it doesn't exist. +- `is_state('device_tracker.paulus', 'home')` will test if the given entity is specified state. +- `is_state_attr('device_tracker.paulus', 'battery', 40)` will test if the given entity is specified state. +- Filter `multiply(x)` will convert the input to a number and multiply it with `x`. +- Filter `round(x)` will convert the input to a number and round it to `x` decimals. +- `now` will be rendered as current time in your time zone. +- `utcnow` will be rendered as UTC time. +- `as_timestamp` will convert datetime object or string to UNIX timestamp +- `distance()` will measure the distance in meters between home, entity, coordinates. +- `closest()` will find the closest entity. +- `relative_time(timestamp)` will format the date time as relative time vs now (ie 7 seconds) +- `float` will format the output as float. ## {% linkable_title Examples %} @@ -93,9 +93,9 @@ Print an attribute if state is defined ```jinja2 {% raw %} {% if states.device_tracker.paulus %} -{{ states.device_tracker.paulus.attributes.battery }} + {{ states.device_tracker.paulus.attributes.battery }} {% else %} -?? + ?? {% endif %}{% endraw %} ``` @@ -119,7 +119,12 @@ Print out a list of all the sensor states. {% if states('sensor.temperature') | float > 20 %} It is warm! -{%endif %}{% endraw %} +{%endif %} + +{{ as_timestamp(states.binary_sensor.garage_door.last_changed) }} + +{{ as_timestamp(now) - as_timestamp(states.binary_sensor.garage_door.last_changed) }} +{% endraw %} ``` ### {% linkable_title Distance examples %} @@ -183,4 +188,11 @@ It depends per component or platform but it is common to be able to define a tem # Extract third prime number {% raw %}{{ value_json.primes[2] }}{% endraw %} + +# Format output +{% raw %}{{ "%+.1f" | value_json }}{% endraw %} + +# Calculations +{% raw %}{{ value_json | multiply(1024) }}{% endraw %} +{% raw %}{{ value_json.used | multiply(0.0001) | round(0) }}{% endraw %} ``` diff --git a/source/demo/dev-tools.html b/source/demo/dev-tools.html index da365c4be94..dae7af1f9f4 100644 --- a/source/demo/dev-tools.html +++ b/source/demo/dev-tools.html @@ -1,43 +1,3 @@ - \ No newline at end of file diff --git a/source/demo/partial-map.html b/source/demo/partial-map.html index 6b6c5c1a15e..0d3aaf12076 100644 --- a/source/demo/partial-map.html +++ b/source/demo/partial-map.html @@ -1,91 +1,4 @@ \ No newline at end of file +case"touchend":return this.addPointerListenerEnd(t,e,i,n);case"touchmove":return this.addPointerListenerMove(t,e,i,n);default:throw"Unknown touch event type"}},addPointerListenerStart:function(t,i,n,s){var a="_leaflet_",r=this._pointers,h=function(t){"mouse"!==t.pointerType&&t.pointerType!==t.MSPOINTER_TYPE_MOUSE&&o.DomEvent.preventDefault(t);for(var e=!1,i=0;i1))&&(this._moved||(o.DomUtil.addClass(e._mapPane,"leaflet-touching"),e.fire("movestart").fire("zoomstart"),this._moved=!0),o.Util.cancelAnimFrame(this._animRequest),this._animRequest=o.Util.requestAnimFrame(this._updateOnMove,this,!0,this._map._container),o.DomEvent.preventDefault(t))}},_updateOnMove:function(){var t=this._map,e=this._getScaleOrigin(),i=t.layerPointToLatLng(e),n=t.getScaleZoom(this._scale);t._animateZoom(i,n,this._startCenter,this._scale,this._delta,!1,!0)},_onTouchEnd:function(){if(!this._moved||!this._zooming)return void(this._zooming=!1);var t=this._map;this._zooming=!1,o.DomUtil.removeClass(t._mapPane,"leaflet-touching"),o.Util.cancelAnimFrame(this._animRequest),o.DomEvent.off(e,"touchmove",this._onTouchMove).off(e,"touchend",this._onTouchEnd);var i=this._getScaleOrigin(),n=t.layerPointToLatLng(i),s=t.getZoom(),a=t.getScaleZoom(this._scale)-s,r=a>0?Math.ceil(a):Math.floor(a),h=t._limitZoom(s+r),l=t.getZoomScale(h)/this._scale;t._animateZoom(n,h,i,l)},_getScaleOrigin:function(){var t=this._centerOffset.subtract(this._delta).divideBy(this._scale);return this._startCenter.add(t)}}),o.Map.addInitHook("addHandler","touchZoom",o.Map.TouchZoom),o.Map.mergeOptions({tap:!0,tapTolerance:15}),o.Map.Tap=o.Handler.extend({addHooks:function(){o.DomEvent.on(this._map._container,"touchstart",this._onDown,this)},removeHooks:function(){o.DomEvent.off(this._map._container,"touchstart",this._onDown,this)},_onDown:function(t){if(t.touches){if(o.DomEvent.preventDefault(t),this._fireClick=!0,t.touches.length>1)return this._fireClick=!1,void clearTimeout(this._holdTimeout);var i=t.touches[0],n=i.target;this._startPos=this._newPos=new o.Point(i.clientX,i.clientY),n.tagName&&"a"===n.tagName.toLowerCase()&&o.DomUtil.addClass(n,"leaflet-active"),this._holdTimeout=setTimeout(o.bind(function(){this._isTapValid()&&(this._fireClick=!1,this._onUp(),this._simulateEvent("contextmenu",i))},this),1e3),o.DomEvent.on(e,"touchmove",this._onMove,this).on(e,"touchend",this._onUp,this)}},_onUp:function(t){if(clearTimeout(this._holdTimeout),o.DomEvent.off(e,"touchmove",this._onMove,this).off(e,"touchend",this._onUp,this),this._fireClick&&t&&t.changedTouches){var i=t.changedTouches[0],n=i.target;n&&n.tagName&&"a"===n.tagName.toLowerCase()&&o.DomUtil.removeClass(n,"leaflet-active"),this._isTapValid()&&this._simulateEvent("click",i)}},_isTapValid:function(){return this._newPos.distanceTo(this._startPos)<=this._map.options.tapTolerance},_onMove:function(t){var e=t.touches[0];this._newPos=new o.Point(e.clientX,e.clientY)},_simulateEvent:function(i,n){var o=e.createEvent("MouseEvents");o._simulated=!0,n.target._simulatedClick=!0,o.initMouseEvent(i,!0,!0,t,1,n.screenX,n.screenY,n.clientX,n.clientY,!1,!1,!1,!1,0,null),n.target.dispatchEvent(o)}}),o.Browser.touch&&!o.Browser.pointer&&o.Map.addInitHook("addHandler","tap",o.Map.Tap),o.Map.mergeOptions({boxZoom:!0}),o.Map.BoxZoom=o.Handler.extend({initialize:function(t){this._map=t,this._container=t._container,this._pane=t._panes.overlayPane,this._moved=!1},addHooks:function(){o.DomEvent.on(this._container,"mousedown",this._onMouseDown,this)},removeHooks:function(){o.DomEvent.off(this._container,"mousedown",this._onMouseDown),this._moved=!1},moved:function(){return this._moved},_onMouseDown:function(t){return this._moved=!1,!(!t.shiftKey||1!==t.which&&1!==t.button)&&(o.DomUtil.disableTextSelection(),o.DomUtil.disableImageDrag(),this._startLayerPoint=this._map.mouseEventToLayerPoint(t),void o.DomEvent.on(e,"mousemove",this._onMouseMove,this).on(e,"mouseup",this._onMouseUp,this).on(e,"keydown",this._onKeyDown,this))},_onMouseMove:function(t){this._moved||(this._box=o.DomUtil.create("div","leaflet-zoom-box",this._pane),o.DomUtil.setPosition(this._box,this._startLayerPoint),this._container.style.cursor="crosshair",this._map.fire("boxzoomstart"));var e=this._startLayerPoint,i=this._box,n=this._map.mouseEventToLayerPoint(t),s=n.subtract(e),a=new o.Point(Math.min(n.x,e.x),Math.min(n.y,e.y));o.DomUtil.setPosition(i,a),this._moved=!0,i.style.width=Math.max(0,Math.abs(s.x)-4)+"px",i.style.height=Math.max(0,Math.abs(s.y)-4)+"px"},_finish:function(){this._moved&&(this._pane.removeChild(this._box),this._container.style.cursor=""),o.DomUtil.enableTextSelection(),o.DomUtil.enableImageDrag(),o.DomEvent.off(e,"mousemove",this._onMouseMove).off(e,"mouseup",this._onMouseUp).off(e,"keydown",this._onKeyDown)},_onMouseUp:function(t){this._finish();var e=this._map,i=e.mouseEventToLayerPoint(t);if(!this._startLayerPoint.equals(i)){var n=new o.LatLngBounds(e.layerPointToLatLng(this._startLayerPoint),e.layerPointToLatLng(i));e.fitBounds(n),e.fire("boxzoomend",{boxZoomBounds:n})}},_onKeyDown:function(t){27===t.keyCode&&this._finish()}}),o.Map.addInitHook("addHandler","boxZoom",o.Map.BoxZoom),o.Map.mergeOptions({keyboard:!0,keyboardPanOffset:80,keyboardZoomOffset:1}),o.Map.Keyboard=o.Handler.extend({keyCodes:{left:[37],right:[39],down:[40],up:[38],zoomIn:[187,107,61,171],zoomOut:[189,109,173]},initialize:function(t){this._map=t,this._setPanOffset(t.options.keyboardPanOffset),this._setZoomOffset(t.options.keyboardZoomOffset)},addHooks:function(){var t=this._map._container;-1===t.tabIndex&&(t.tabIndex="0"),o.DomEvent.on(t,"focus",this._onFocus,this).on(t,"blur",this._onBlur,this).on(t,"mousedown",this._onMouseDown,this),this._map.on("focus",this._addHooks,this).on("blur",this._removeHooks,this)},removeHooks:function(){this._removeHooks();var t=this._map._container;o.DomEvent.off(t,"focus",this._onFocus,this).off(t,"blur",this._onBlur,this).off(t,"mousedown",this._onMouseDown,this),this._map.off("focus",this._addHooks,this).off("blur",this._removeHooks,this)},_onMouseDown:function(){if(!this._focused){var i=e.body,n=e.documentElement,o=i.scrollTop||n.scrollTop,s=i.scrollLeft||n.scrollLeft;this._map._container.focus(),t.scrollTo(s,o)}},_onFocus:function(){this._focused=!0,this._map.fire("focus")},_onBlur:function(){this._focused=!1,this._map.fire("blur")},_setPanOffset:function(t){var e,i,n=this._panKeys={},o=this.keyCodes;for(e=0,i=o.left.length;i>e;e++)n[o.left[e]]=[-1*t,0];for(e=0,i=o.right.length;i>e;e++)n[o.right[e]]=[t,0];for(e=0,i=o.down.length;i>e;e++)n[o.down[e]]=[0,t];for(e=0,i=o.up.length;i>e;e++)n[o.up[e]]=[0,-1*t]},_setZoomOffset:function(t){var e,i,n=this._zoomKeys={},o=this.keyCodes;for(e=0,i=o.zoomIn.length;i>e;e++)n[o.zoomIn[e]]=t;for(e=0,i=o.zoomOut.length;i>e;e++)n[o.zoomOut[e]]=-t},_addHooks:function(){o.DomEvent.on(e,"keydown",this._onKeyDown,this)},_removeHooks:function(){o.DomEvent.off(e,"keydown",this._onKeyDown,this)},_onKeyDown:function(t){var e=t.keyCode,i=this._map;if(e in this._panKeys){if(i._panAnim&&i._panAnim._inProgress)return;i.panBy(this._panKeys[e]),i.options.maxBounds&&i.panInsideBounds(i.options.maxBounds)}else{if(!(e in this._zoomKeys))return;i.setZoom(i.getZoom()+this._zoomKeys[e])}o.DomEvent.stop(t)}}),o.Map.addInitHook("addHandler","keyboard",o.Map.Keyboard),o.Handler.MarkerDrag=o.Handler.extend({initialize:function(t){this._marker=t},addHooks:function(){var t=this._marker._icon;this._draggable||(this._draggable=new o.Draggable(t,t)),this._draggable.on("dragstart",this._onDragStart,this).on("drag",this._onDrag,this).on("dragend",this._onDragEnd,this),this._draggable.enable(),o.DomUtil.addClass(this._marker._icon,"leaflet-marker-draggable")},removeHooks:function(){this._draggable.off("dragstart",this._onDragStart,this).off("drag",this._onDrag,this).off("dragend",this._onDragEnd,this),this._draggable.disable(),o.DomUtil.removeClass(this._marker._icon,"leaflet-marker-draggable")},moved:function(){return this._draggable&&this._draggable._moved},_onDragStart:function(){this._marker.closePopup().fire("movestart").fire("dragstart")},_onDrag:function(){var t=this._marker,e=t._shadow,i=o.DomUtil.getPosition(t._icon),n=t._map.layerPointToLatLng(i);e&&o.DomUtil.setPosition(e,i),t._latlng=n,t.fire("move",{latlng:n}).fire("drag")},_onDragEnd:function(t){this._marker.fire("moveend").fire("dragend",t)}}),o.Control=o.Class.extend({options:{position:"topright"},initialize:function(t){o.setOptions(this,t)},getPosition:function(){return this.options.position},setPosition:function(t){var e=this._map;return e&&e.removeControl(this),this.options.position=t,e&&e.addControl(this),this},getContainer:function(){return this._container},addTo:function(t){this._map=t;var e=this._container=this.onAdd(t),i=this.getPosition(),n=t._controlCorners[i];return o.DomUtil.addClass(e,"leaflet-control"),-1!==i.indexOf("bottom")?n.insertBefore(e,n.firstChild):n.appendChild(e),this},removeFrom:function(t){var e=this.getPosition(),i=t._controlCorners[e];return i.removeChild(this._container),this._map=null,this.onRemove&&this.onRemove(t),this},_refocusOnMap:function(){this._map&&this._map.getContainer().focus()}}),o.control=function(t){return new o.Control(t)},o.Map.include({addControl:function(t){return t.addTo(this),this},removeControl:function(t){return t.removeFrom(this),this},_initControlPos:function(){function t(t,s){var a=i+t+" "+i+s;e[t+s]=o.DomUtil.create("div",a,n)}var e=this._controlCorners={},i="leaflet-",n=this._controlContainer=o.DomUtil.create("div",i+"control-container",this._container);t("top","left"),t("top","right"),t("bottom","left"),t("bottom","right")},_clearControlPos:function(){this._container.removeChild(this._controlContainer)}}),o.Control.Zoom=o.Control.extend({options:{position:"topleft",zoomInText:"+",zoomInTitle:"Zoom in",zoomOutText:"-",zoomOutTitle:"Zoom out"},onAdd:function(t){var e="leaflet-control-zoom",i=o.DomUtil.create("div",e+" leaflet-bar");return this._map=t,this._zoomInButton=this._createButton(this.options.zoomInText,this.options.zoomInTitle,e+"-in",i,this._zoomIn,this),this._zoomOutButton=this._createButton(this.options.zoomOutText,this.options.zoomOutTitle,e+"-out",i,this._zoomOut,this),this._updateDisabled(),t.on("zoomend zoomlevelschange",this._updateDisabled,this),i},onRemove:function(t){t.off("zoomend zoomlevelschange",this._updateDisabled,this)},_zoomIn:function(t){this._map.zoomIn(t.shiftKey?3:1)},_zoomOut:function(t){this._map.zoomOut(t.shiftKey?3:1)},_createButton:function(t,e,i,n,s,a){var r=o.DomUtil.create("a",i,n);r.innerHTML=t,r.href="#",r.title=e;var h=o.DomEvent.stopPropagation;return o.DomEvent.on(r,"click",h).on(r,"mousedown",h).on(r,"dblclick",h).on(r,"click",o.DomEvent.preventDefault).on(r,"click",s,a).on(r,"click",this._refocusOnMap,a),r},_updateDisabled:function(){var t=this._map,e="leaflet-disabled";o.DomUtil.removeClass(this._zoomInButton,e),o.DomUtil.removeClass(this._zoomOutButton,e),t._zoom===t.getMinZoom()&&o.DomUtil.addClass(this._zoomOutButton,e),t._zoom===t.getMaxZoom()&&o.DomUtil.addClass(this._zoomInButton,e)}}),o.Map.mergeOptions({zoomControl:!0}),o.Map.addInitHook(function(){this.options.zoomControl&&(this.zoomControl=new o.Control.Zoom,this.addControl(this.zoomControl))}),o.control.zoom=function(t){return new o.Control.Zoom(t)},o.Control.Attribution=o.Control.extend({options:{position:"bottomright",prefix:'Leaflet'},initialize:function(t){o.setOptions(this,t),this._attributions={}},onAdd:function(t){this._container=o.DomUtil.create("div","leaflet-control-attribution"),o.DomEvent.disableClickPropagation(this._container);for(var e in t._layers)t._layers[e].getAttribution&&this.addAttribution(t._layers[e].getAttribution());return t.on("layeradd",this._onLayerAdd,this).on("layerremove",this._onLayerRemove,this),this._update(),this._container},onRemove:function(t){t.off("layeradd",this._onLayerAdd).off("layerremove",this._onLayerRemove)},setPrefix:function(t){return this.options.prefix=t,this._update(),this},addAttribution:function(t){return t?(this._attributions[t]||(this._attributions[t]=0),this._attributions[t]++,this._update(),this):void 0},removeAttribution:function(t){return t?(this._attributions[t]&&(this._attributions[t]--,this._update()),this):void 0},_update:function(){if(this._map){var t=[];for(var e in this._attributions)this._attributions[e]&&t.push(e);var i=[];this.options.prefix&&i.push(this.options.prefix),t.length&&i.push(t.join(", ")),this._container.innerHTML=i.join(" | ")}},_onLayerAdd:function(t){t.layer.getAttribution&&this.addAttribution(t.layer.getAttribution())},_onLayerRemove:function(t){t.layer.getAttribution&&this.removeAttribution(t.layer.getAttribution())}}),o.Map.mergeOptions({attributionControl:!0}),o.Map.addInitHook(function(){this.options.attributionControl&&(this.attributionControl=(new o.Control.Attribution).addTo(this))}),o.control.attribution=function(t){return new o.Control.Attribution(t)},o.Control.Scale=o.Control.extend({options:{position:"bottomleft",maxWidth:100,metric:!0,imperial:!0,updateWhenIdle:!1},onAdd:function(t){this._map=t;var e="leaflet-control-scale",i=o.DomUtil.create("div",e),n=this.options;return this._addScales(n,e,i),t.on(n.updateWhenIdle?"moveend":"move",this._update,this),t.whenReady(this._update,this),i},onRemove:function(t){t.off(this.options.updateWhenIdle?"moveend":"move",this._update,this)},_addScales:function(t,e,i){t.metric&&(this._mScale=o.DomUtil.create("div",e+"-line",i)),t.imperial&&(this._iScale=o.DomUtil.create("div",e+"-line",i))},_update:function(){var t=this._map.getBounds(),e=t.getCenter().lat,i=6378137*Math.PI*Math.cos(e*Math.PI/180),n=i*(t.getNorthEast().lng-t.getSouthWest().lng)/180,o=this._map.getSize(),s=this.options,a=0;o.x>0&&(a=n*(s.maxWidth/o.x)),this._updateScales(s,a)},_updateScales:function(t,e){t.metric&&e&&this._updateMetric(e),t.imperial&&e&&this._updateImperial(e)},_updateMetric:function(t){var e=this._getRoundNum(t);this._mScale.style.width=this._getScaleWidth(e/t)+"px",this._mScale.innerHTML=1e3>e?e+" m":e/1e3+" km"},_updateImperial:function(t){var e,i,n,o=3.2808399*t,s=this._iScale;o>5280?(e=o/5280,i=this._getRoundNum(e),s.style.width=this._getScaleWidth(i/e)+"px",s.innerHTML=i+" mi"):(n=this._getRoundNum(o),s.style.width=this._getScaleWidth(n/o)+"px",s.innerHTML=n+" ft")},_getScaleWidth:function(t){return Math.round(this.options.maxWidth*t)-10},_getRoundNum:function(t){var e=Math.pow(10,(Math.floor(t)+"").length-1),i=t/e;return i=i>=10?10:i>=5?5:i>=3?3:i>=2?2:1,e*i}}),o.control.scale=function(t){return new o.Control.Scale(t)},o.Control.Layers=o.Control.extend({options:{collapsed:!0,position:"topright",autoZIndex:!0},initialize:function(t,e,i){o.setOptions(this,i),this._layers={},this._lastZIndex=0,this._handlingClick=!1;for(var n in t)this._addLayer(t[n],n);for(n in e)this._addLayer(e[n],n,!0)},onAdd:function(t){return this._initLayout(),this._update(),t.on("layeradd",this._onLayerChange,this).on("layerremove",this._onLayerChange,this),this._container},onRemove:function(t){t.off("layeradd",this._onLayerChange,this).off("layerremove",this._onLayerChange,this)},addBaseLayer:function(t,e){return this._addLayer(t,e),this._update(),this},addOverlay:function(t,e){return this._addLayer(t,e,!0),this._update(),this},removeLayer:function(t){var e=o.stamp(t);return delete this._layers[e],this._update(),this},_initLayout:function(){var t="leaflet-control-layers",e=this._container=o.DomUtil.create("div",t);e.setAttribute("aria-haspopup",!0),o.Browser.touch?o.DomEvent.on(e,"click",o.DomEvent.stopPropagation):o.DomEvent.disableClickPropagation(e).disableScrollPropagation(e);var i=this._form=o.DomUtil.create("form",t+"-list");if(this.options.collapsed){o.Browser.android||o.DomEvent.on(e,"mouseover",this._expand,this).on(e,"mouseout",this._collapse,this);var n=this._layersLink=o.DomUtil.create("a",t+"-toggle",e);n.href="#",n.title="Layers",o.Browser.touch?o.DomEvent.on(n,"click",o.DomEvent.stop).on(n,"click",this._expand,this):o.DomEvent.on(n,"focus",this._expand,this),o.DomEvent.on(i,"click",function(){setTimeout(o.bind(this._onInputClick,this),0)},this),this._map.on("click",this._collapse,this)}else this._expand();this._baseLayersList=o.DomUtil.create("div",t+"-base",i),this._separator=o.DomUtil.create("div",t+"-separator",i),this._overlaysList=o.DomUtil.create("div",t+"-overlays",i),e.appendChild(i)},_addLayer:function(t,e,i){var n=o.stamp(t);this._layers[n]={layer:t,name:e,overlay:i},this.options.autoZIndex&&t.setZIndex&&(this._lastZIndex++,t.setZIndex(this._lastZIndex))},_update:function(){if(this._container){this._baseLayersList.innerHTML="",this._overlaysList.innerHTML="";var t,e,i=!1,n=!1;for(t in this._layers)e=this._layers[t],this._addItem(e),n=n||e.overlay,i=i||!e.overlay;this._separator.style.display=n&&i?"":"none"}},_onLayerChange:function(t){var e=this._layers[o.stamp(t.layer)];if(e){this._handlingClick||this._update();var i=e.overlay?"layeradd"===t.type?"overlayadd":"overlayremove":"layeradd"===t.type?"baselayerchange":null;i&&this._map.fire(i,e)}},_createRadioElement:function(t,i){var n='t;t++)e=n[t],i=this._layers[e.layerId],e.checked&&!this._map.hasLayer(i.layer)?this._map.addLayer(i.layer):!e.checked&&this._map.hasLayer(i.layer)&&this._map.removeLayer(i.layer);this._handlingClick=!1,this._refocusOnMap()},_expand:function(){o.DomUtil.addClass(this._container,"leaflet-control-layers-expanded")},_collapse:function(){this._container.className=this._container.className.replace(" leaflet-control-layers-expanded","")}}),o.control.layers=function(t,e,i){return new o.Control.Layers(t,e,i)},o.PosAnimation=o.Class.extend({includes:o.Mixin.Events,run:function(t,e,i,n){this.stop(),this._el=t,this._inProgress=!0,this._newPos=e,this.fire("start"),t.style[o.DomUtil.TRANSITION]="all "+(i||.25)+"s cubic-bezier(0,0,"+(n||.5)+",1)",o.DomEvent.on(t,o.DomUtil.TRANSITION_END,this._onTransitionEnd,this),o.DomUtil.setPosition(t,e),o.Util.falseFn(t.offsetWidth),this._stepTimer=setInterval(o.bind(this._onStep,this),50)},stop:function(){this._inProgress&&(o.DomUtil.setPosition(this._el,this._getPos()),this._onTransitionEnd(),o.Util.falseFn(this._el.offsetWidth))},_onStep:function(){var t=this._getPos();return t?(this._el._leaflet_pos=t,void this.fire("step")):void this._onTransitionEnd()},_transformRe:/([-+]?(?:\d*\.)?\d+)\D*, ([-+]?(?:\d*\.)?\d+)\D*\)/,_getPos:function(){var e,i,n,s=this._el,a=t.getComputedStyle(s);if(o.Browser.any3d){if(n=a[o.DomUtil.TRANSFORM].match(this._transformRe),!n)return;e=parseFloat(n[1]),i=parseFloat(n[2])}else e=parseFloat(a.left),i=parseFloat(a.top);return new o.Point(e,i,(!0))},_onTransitionEnd:function(){o.DomEvent.off(this._el,o.DomUtil.TRANSITION_END,this._onTransitionEnd,this),this._inProgress&&(this._inProgress=!1,this._el.style[o.DomUtil.TRANSITION]="",this._el._leaflet_pos=this._newPos,clearInterval(this._stepTimer),this.fire("step").fire("end"))}}),o.Map.include({setView:function(t,e,n){if(e=e===i?this._zoom:this._limitZoom(e),t=this._limitCenter(o.latLng(t),e,this.options.maxBounds),n=n||{},this._panAnim&&this._panAnim.stop(),this._loaded&&!n.reset&&n!==!0){n.animate!==i&&(n.zoom=o.extend({animate:n.animate},n.zoom),n.pan=o.extend({animate:n.animate},n.pan));var s=this._zoom!==e?this._tryAnimatedZoom&&this._tryAnimatedZoom(t,e,n.zoom):this._tryAnimatedPan(t,n.pan);if(s)return clearTimeout(this._sizeTimer),this}return this._resetView(t,e),this},panBy:function(t,e){if(t=o.point(t).round(),e=e||{},!t.x&&!t.y)return this;if(this._panAnim||(this._panAnim=new o.PosAnimation,this._panAnim.on({step:this._onPanTransitionStep,end:this._onPanTransitionEnd},this)),e.noMoveStart||this.fire("movestart"),e.animate!==!1){o.DomUtil.addClass(this._mapPane,"leaflet-pan-anim");var i=this._getMapPanePos().subtract(t);this._panAnim.run(this._mapPane,i,e.duration||.25,e.easeLinearity)}else this._rawPanBy(t),this.fire("move").fire("moveend");return this},_onPanTransitionStep:function(){this.fire("move")},_onPanTransitionEnd:function(){o.DomUtil.removeClass(this._mapPane,"leaflet-pan-anim"),this.fire("moveend")},_tryAnimatedPan:function(t,e){var i=this._getCenterOffset(t)._floor();return!((e&&e.animate)!==!0&&!this.getSize().contains(i))&&(this.panBy(i,e),!0)}}),o.PosAnimation=o.DomUtil.TRANSITION?o.PosAnimation:o.PosAnimation.extend({run:function(t,e,i,n){this.stop(),this._el=t,this._inProgress=!0,this._duration=i||.25,this._easeOutPower=1/Math.max(n||.5,.2),this._startPos=o.DomUtil.getPosition(t),this._offset=e.subtract(this._startPos),this._startTime=+new Date,this.fire("start"),this._animate()},stop:function(){this._inProgress&&(this._step(),this._complete())},_animate:function(){this._animId=o.Util.requestAnimFrame(this._animate,this),this._step()},_step:function(){var t=+new Date-this._startTime,e=1e3*this._duration;e>t?this._runFrame(this._easeOut(t/e)):(this._runFrame(1),this._complete())},_runFrame:function(t){var e=this._startPos.add(this._offset.multiplyBy(t));o.DomUtil.setPosition(this._el,e),this.fire("step")},_complete:function(){o.Util.cancelAnimFrame(this._animId),this._inProgress=!1,this.fire("end")},_easeOut:function(t){return 1-Math.pow(1-t,this._easeOutPower)}}),o.Map.mergeOptions({zoomAnimation:!0,zoomAnimationThreshold:4}),o.DomUtil.TRANSITION&&o.Map.addInitHook(function(){this._zoomAnimated=this.options.zoomAnimation&&o.DomUtil.TRANSITION&&o.Browser.any3d&&!o.Browser.android23&&!o.Browser.mobileOpera,this._zoomAnimated&&o.DomEvent.on(this._mapPane,o.DomUtil.TRANSITION_END,this._catchTransitionEnd,this)}),o.Map.include(o.DomUtil.TRANSITION?{_catchTransitionEnd:function(t){this._animatingZoom&&t.propertyName.indexOf("transform")>=0&&this._onZoomTransitionEnd()},_nothingToAnimate:function(){return!this._container.getElementsByClassName("leaflet-zoom-animated").length},_tryAnimatedZoom:function(t,e,i){if(this._animatingZoom)return!0;if(i=i||{},!this._zoomAnimated||i.animate===!1||this._nothingToAnimate()||Math.abs(e-this._zoom)>this.options.zoomAnimationThreshold)return!1;var n=this.getZoomScale(e),o=this._getCenterOffset(t)._divideBy(1-1/n),s=this._getCenterLayerPoint()._add(o);return!(i.animate!==!0&&!this.getSize().contains(o))&&(this.fire("movestart").fire("zoomstart"),this._animateZoom(t,e,s,n,null,!0),!0)},_animateZoom:function(t,e,i,n,s,a,r){r||(this._animatingZoom=!0),o.DomUtil.addClass(this._mapPane,"leaflet-zoom-anim"),this._animateToCenter=t,this._animateToZoom=e,o.Draggable&&(o.Draggable._disabled=!0),o.Util.requestAnimFrame(function(){this.fire("zoomanim",{center:t,zoom:e,origin:i,scale:n,delta:s,backwards:a}),setTimeout(o.bind(this._onZoomTransitionEnd,this),250)},this)},_onZoomTransitionEnd:function(){this._animatingZoom&&(this._animatingZoom=!1,o.DomUtil.removeClass(this._mapPane,"leaflet-zoom-anim"),o.Util.requestAnimFrame(function(){this._resetView(this._animateToCenter,this._animateToZoom,!0,!0),o.Draggable&&(o.Draggable._disabled=!1)},this))}}:{}),o.TileLayer.include({_animateZoom:function(t){this._animating||(this._animating=!0,this._prepareBgBuffer());var e=this._bgBuffer,i=o.DomUtil.TRANSFORM,n=t.delta?o.DomUtil.getTranslateString(t.delta):e.style[i],s=o.DomUtil.getScaleString(t.scale,t.origin);e.style[i]=t.backwards?s+" "+n:n+" "+s},_endZoomAnim:function(){var t=this._tileContainer,e=this._bgBuffer;t.style.visibility="",t.parentNode.appendChild(t),o.Util.falseFn(e.offsetWidth);var i=this._map.getZoom();(i>this.options.maxZoom||i.5&&.5>n?(t.style.visibility="hidden",void this._stopLoadingImages(t)):(e.style.visibility="hidden",e.style[o.DomUtil.TRANSFORM]="",this._tileContainer=e,e=this._bgBuffer=t,this._stopLoadingImages(e),void clearTimeout(this._clearBgBufferTimer))},_getLoadedTilesPercentage:function(t){var e,i,n=t.getElementsByTagName("img"),o=0;for(e=0,i=n.length;i>e;e++)n[e].complete&&o++;return o/i},_stopLoadingImages:function(t){var e,i,n,s=Array.prototype.slice.call(t.getElementsByTagName("img"));for(e=0,i=s.length;i>e;e++)n=s[e],n.complete||(n.onload=o.Util.falseFn,n.onerror=o.Util.falseFn,n.src=o.Util.emptyImageUrl,n.parentNode.removeChild(n))}}),o.Map.include({_defaultLocateOptions:{watch:!1,setView:!1,maxZoom:1/0,timeout:1e4,maximumAge:0,enableHighAccuracy:!1},locate:function(t){if(t=this._locateOptions=o.extend(this._defaultLocateOptions,t),!navigator.geolocation)return this._handleGeolocationError({code:0,message:"Geolocation not supported."}),this;var e=o.bind(this._handleGeolocationResponse,this),i=o.bind(this._handleGeolocationError,this);return t.watch?this._locationWatchId=navigator.geolocation.watchPosition(e,i,t):navigator.geolocation.getCurrentPosition(e,i,t),this},stopLocate:function(){return navigator.geolocation&&navigator.geolocation.clearWatch(this._locationWatchId),this._locateOptions&&(this._locateOptions.setView=!1),this},_handleGeolocationError:function(t){var e=t.code,i=t.message||(1===e?"permission denied":2===e?"position unavailable":"timeout");this._locateOptions.setView&&!this._loaded&&this.fitWorld(),this.fire("locationerror",{code:e,message:"Geolocation error: "+i+"."})},_handleGeolocationResponse:function(t){var e=t.coords.latitude,i=t.coords.longitude,n=new o.LatLng(e,i),s=180*t.coords.accuracy/40075017,a=s/Math.cos(o.LatLng.DEG_TO_RAD*e),r=o.latLngBounds([e-s,i-a],[e+s,i+a]),h=this._locateOptions;if(h.setView){var l=Math.min(this.getBoundsZoom(r),h.maxZoom);this.setView(n,l)}var u={latlng:n,bounds:r,timestamp:t.timestamp};for(var c in t.coords)"number"==typeof t.coords[c]&&(u[c]=t.coords[c]);this.fire("locationfound",u)}})}(window,document)- \ No newline at end of file diff --git a/source/developers/credits.markdown b/source/developers/credits.markdown index 7ed4da5e73b..7b2923634b8 100644 --- a/source/developers/credits.markdown +++ b/source/developers/credits.markdown @@ -51,7 +51,7 @@ This page contains a list of people who have contributed in one way or another t - [Dale Higgs](https://github.com/dale3h) - [Dan Cinnamon](https://github.com/Cinntax) - [Daniel Perna](https://github.com/danielperna84) -- [Daniel Iversen](https://github.com/danielhiversen) +- [Daniel Høyer Iversen](https://github.com/danielhiversen) - [Daniel J. Kemp](https://github.com/danieljkemp/) - [Daniel Matuschek](https://github.com/usul27) - [Dan Smith](https://github.com/kk7ds) diff --git a/source/developers/platform_example_light.markdown b/source/developers/platform_example_light.markdown index 7d022c024da..c50e2ca6e4a 100644 --- a/source/developers/platform_example_light.markdown +++ b/source/developers/platform_example_light.markdown @@ -11,6 +11,20 @@ footer: true This example is for adding support for the imaginary Awesome Lights. It shows the different best practices for developing a platform. +Similar to Example Sensor Platform, Copy the code below and create it as a file in `/custom_components/light/awesomelights.py`. + +Add the following to your configuration.yaml: + +```yaml +light: + - platform: awesomelights + host: HOST_HERE + username: USERNAME_HERE + password: PASSWORD_HERE_OR_secrets.yaml +``` + +Note the `platform` name matches the filename for the source code. + ```python import logging diff --git a/source/developers/platform_example_sensor.markdown b/source/developers/platform_example_sensor.markdown index 6320d3870c5..124475f9661 100644 --- a/source/developers/platform_example_sensor.markdown +++ b/source/developers/platform_example_sensor.markdown @@ -37,7 +37,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): class ExampleSensor(Entity): @property def name(self): - return 'Temperature' + return 'Example Temperature' @property def state(self): diff --git a/source/developers/releasing.markdown b/source/developers/releasing.markdown index fd8b886638b..e9cff8ecd94 100644 --- a/source/developers/releasing.markdown +++ b/source/developers/releasing.markdown @@ -15,8 +15,10 @@ This page describes the steps for publishing a new Home Assistant release. 1. Create a pull request from `dev` to `master` with the upcoming release number as title. 2. Merge `master` into `dev` to make the PR mergable. PR message contains intro, highlighting major changes, and an overview of all changes tagging each author. -3. Merge pull request. -4. Go to [releases](https://github.com/home-assistant/home-assistant/releases) and tag a new release on the `master` branch. Tag name and title name are version number. Release description is text from PR. +3. Update `homeassistant/const.py` with the correct version number (remove the the `dev` tag) and push that commit. +4. Merge pull request. +5. Then, after merged, push another update to `dev` of `homeassistant/const.py` that includes the next version with the `dev` tag. Add a meaningful commit message like "Version bump to X". This commit acts as marker for the next release. +6. Go to [releases](https://github.com/home-assistant/home-assistant/releases) and tag a new release on the `master` branch. "Tag version" and "Release title" are the version number (`O.x` for major version, `0.x.y` for minor and bug fix releases). Release description is the text from PR. Press "Publish release" to finish the process. ### {% linkable_title Website %} @@ -29,7 +31,7 @@ This page describes the steps for publishing a new Home Assistant release. ### {% linkable_title Python Package Index %} -Checkout `master` branch and run `script/release` to publish the new release on [Python Package Index](https://pypi.python.org) +Checkout the `master` branch and run `script/release` to publish the new release on [Python Package Index](https://pypi.python.org). ### {% linkable_title Social media %} diff --git a/source/developers/server_sent_events.markdown b/source/developers/server_sent_events.markdown index 52dd28a42b2..86dc50952aa 100644 --- a/source/developers/server_sent_events.markdown +++ b/source/developers/server_sent_events.markdown @@ -44,6 +44,12 @@ Visit [http://localhost:8123/local/sse.html](http://localhost:8123/local/sse.htm ## {% linkable_title Examples %} +The simplest way to consume server-sent events is `curl`. + +```bash +$ curl http://localhost:8123/api/stream?api_password=MYPASS +``` + ### {% linkable_title Website %} The [home-assistant-sse](https://github.com/fabaff/home-assistant-sse) repository contains an more advanced example. diff --git a/source/getting-started/basic.markdown b/source/getting-started/basic.markdown index afae49f87e0..6eadc61a870 100644 --- a/source/getting-started/basic.markdown +++ b/source/getting-started/basic.markdown @@ -50,11 +50,15 @@ Home Assistant runs as a self-hosted web application and contains support to be ### {% linkable_title Remote access %} -To make Home Assistant accessible while away from home, you will have to setup port forwarding from your router to port 8123 on the computer that is hosting Home Assistant. Instructions on how to do this can be found by searching ` port forwarding instructions`. +To make Home Assistant accessible while away from home, you will have to make it accessible. -Some Internet service providers will only offer dynamic IPs. This can cause you to be unable to access Home Assistant while away. You can solve this by using a free Dynamic DNS service like [DuckDNS](https://www.duckdns.org/). +The common approach is to setup port forwarding from your router to port 8123 on the computer that is hosting Home Assistant. Instructions on how to do this can be found by searching ` port forwarding instructions`. -You should definitely consider to encrypt your traffic if you are accessing your Home Assistant installation from abroad. For details please check the [Set up encryption using Let's Encrypt](/blog/2015/12/13/setup-encryption-using-lets-encrypt/) blog post. +The problem with making a port accessible is that some Internet service providers will only offer dynamic IPs. This can cause you to be unable to access Home Assistant while away. You can solve this by using a free Dynamic DNS service like [DuckDNS](https://www.duckdns.org/). + +Just putting a port up is not secure. You should definitely consider to encrypt your traffic if you are accessing your Home Assistant installation from abroad. For details please check the [Set up encryption using Let's Encrypt](/blog/2015/12/13/setup-encryption-using-lets-encrypt/) blog post. + +If you want the very best security, check out [the instructions how to use Tor to access your home](/cookbook/tor_configuration/). ### [Next step: Setting up devices »](/getting-started/devices/) diff --git a/source/getting-started/installation-vagrant.markdown b/source/getting-started/installation-vagrant.markdown index 70a55196aa8..1a1ee99cb10 100644 --- a/source/getting-started/installation-vagrant.markdown +++ b/source/getting-started/installation-vagrant.markdown @@ -38,7 +38,7 @@ The following instructions will assume you changed your working directory to be vagrant up ``` -This will download + start a virtual machine using Virtualbox, which will internally setup the development environment necessary to start Home Assistant process and run test suite as well. After the VM has started succesfully, Home Assistant frontend will be accessible locally from your browser at [http://localhost:8123](http://localhost:8123) +This will download and start a virtual machine using Virtualbox, which will internally setup the development environment necessary to start Home Assistant process and run test suite as well. After the VM has started succesfully, the Home Assistant frontend will be accessible locally from your browser at [http://localhost:8123](http://localhost:8123) ## {% linkable_title Stopping Vagrant %} @@ -52,9 +52,9 @@ To start it again, just run `vagrant up` ## {% linkable_title Restarting Home Assistant process to test changes %} -The root `home-assistant` directory on your workstation will be mirrored with `/home-assistant` inside the VM. In `virtualization/vagrant` there's also a `config` folder that you can use to drop configuration files ([here](https://home-assistant.io/getting-started/configuration/) you can find more information on how to configure HASS). +The root `home-assistant` directory on your workstation will be mirrored with `/home-assistant` inside the VM. In `virtualization/vagrant` there's also a `config` folder that you can use to drop configuration files (Check the [Getting started section](https://home-assistant.io/getting-started/configuration/) for more information about how to configure Home Assistant). -Any changes made to the local directory on your workstation will be available from the Vagrant host, so to apply your changes to the HASS process, just restart it: +Any changes made to the local directory on your workstation will be available from the Vagrant host, so to apply your changes to the Home Assistant process, just restart it: ```bash touch restart ; vagrant provision diff --git a/source/getting-started/updating.markdown b/source/getting-started/updating.markdown index ac47300d95f..b67dbf4e2a3 100644 --- a/source/getting-started/updating.markdown +++ b/source/getting-started/updating.markdown @@ -16,7 +16,7 @@ The default way to update Home Assistant to the latest release, when available, $ pip3 install --upgrade homeassistant ``` -Different installation methods as [Raspberry Pi All-In-One Installer](/getting-started/installation-raspberry-pi-all-in-one/#upgrading), [Vagrant](/getting-started/installation-vagrant/), or [Virtualenv](/getting-started/installation-virtualenv/#upgrading-home-assistant) may have a alternative way for updating Home Assistant. +Different installation methods as [Raspberry Pi All-In-One Installer](/getting-started/installation-raspberry-pi-all-in-one/#upgrading), [Vagrant](/getting-started/installation-vagrant/), or [Virtualenv](/getting-started/installation-virtualenv/#upgrading-home-assistant) may have an alternative way for updating Home Assistant. After updating, restart Home Assistant for the changes to take effect. This means that you have to restart `hass` itself or the [autostarting](/getting-started/autostart/) daemon if you use any. diff --git a/source/images/blog/2016-07-reporting/db-browser.png b/source/images/blog/2016-07-reporting/db-browser.png new file mode 100644 index 00000000000..9c92db4f047 Binary files /dev/null and b/source/images/blog/2016-07-reporting/db-browser.png differ diff --git a/source/images/blog/2016-07-reporting/libreoffice-graph.png b/source/images/blog/2016-07-reporting/libreoffice-graph.png new file mode 100644 index 00000000000..3612dbebd2a Binary files /dev/null and b/source/images/blog/2016-07-reporting/libreoffice-graph.png differ diff --git a/source/images/blog/2016-07-reporting/libreoffice-import.png b/source/images/blog/2016-07-reporting/libreoffice-import.png new file mode 100644 index 00000000000..a7188add46e Binary files /dev/null and b/source/images/blog/2016-07-reporting/libreoffice-import.png differ diff --git a/source/images/blog/2016-07-reporting/mpl-sensor.png b/source/images/blog/2016-07-reporting/mpl-sensor.png new file mode 100644 index 00000000000..3cf61c0ddaa Binary files /dev/null and b/source/images/blog/2016-07-reporting/mpl-sensor.png differ diff --git a/source/images/screenshots/plex-configure.png b/source/images/screenshots/plex-configure.png new file mode 100644 index 00000000000..459d2c2ab22 Binary files /dev/null and b/source/images/screenshots/plex-configure.png differ diff --git a/source/images/screenshots/plex-token.png b/source/images/screenshots/plex-token.png new file mode 100644 index 00000000000..fcbfa7d442e Binary files /dev/null and b/source/images/screenshots/plex-token.png differ diff --git a/source/index.html b/source/index.html index 90061cfdd1b..1382fa6d062 100644 --- a/source/index.html +++ b/source/index.html @@ -15,8 +15,13 @@ hide_github_edit: true