📜 ⬆️ ⬇️

“Hi, Siri. Turn on the heaters "- Integration of NooLite smart home with Apple HomeKit

image

In my first article I described the background to the emergence of a remote heating control system in a country house through a Telegram bot, which my family and I have used for a long time.


With the release of iOS 10, Apple introduced the Home application to users - its implementation of the smart home control interface via HomeKit . I was very interested in this topic and, having spent several evenings studying the available material, I decided to implement the integration of this product with my system. In the article I will detail the process of its installation and configuration, as well as share the video with the results of what happened in the end.


Content


  1. Introduction
  2. Installing and configuring OpenHab
  3. Connecting NooLite to OpenHab
  4. Install and configure HomeKit for OpenHab
  5. Setting up the Home application
  6. Result
  7. Conclusion


Introduction


To understand the source data and the initial configuration of the smart home, I advise you to read the first article .

The first task was the search for ready-made free solutions in home automation with the support of HomeKit. Then I immediately remembered several articles on the open source product OpenHab. After reading the documentation and a little googling, I really found an addon to support the HomeKit protocol. At the moment, the second version of OpenHab is being prepared for release and active beta testing is underway. The latest available version at that time was beta4 , which I decided to use.


The integration process can be divided into 4 stages:


  1. Installing and configuring OpenHab
  2. Connecting NooLite to OpenHab
  3. Install and configure HomeKit for OpenHab
  4. Customize the Home app

The result would be the following scheme:
image





Installing and configuring OpenHab


OpenHab 2 has quite detailed documentation , where in addition to the main platforms there is also a tutorial on installing on the Raspberry Pi. I will not copy all the steps here, since there were no problems with the installation.


After installation, the OpenHab web interface was available in the browser at the address: http: // <device_address_c_openhab>: 8080 .


image


Immediately available:


  1. Basic UI, Classic UI - control panels for devices connected to OpenHab
  2. Rest API - rest API itself
  3. Paper UI - OpenHab admin interface, through which it can be configured

While the Basic UI was empty:


image





Connecting NooLite to OpenHab


Based on the documentation, to connect a new device to OpenHab, it was necessary:


  1. Add it to items
  2. Add to sitemap so that it is displayed in the OpenHab smart home control panel (Basic UI, Classic UI). You can skip this step, it does not apply to working with HomeKit, but with its help you can check that OpenHab sees NooLite and works with it correctly.
  3. Add rules to rules if you need automation or additional event processing logic

Items


In items, we describe the final managed devices, for example, the NooLite power unit and "teach" OpenHab to work with it:


itemtype itemname ["labeltext"] [<iconname>] [(group1, group2, ...)] [{bindingconfig}] 

Where:



The most interesting thing here is binding. How the interaction with the managed device will be performed. After a bit of searching for information on how OpenHab works with NooLite, I found a ready - made version . But it was written to work with the NooLite USB adapters of the PC and RX series. In my own system, the management of the power units worked through the PR1132 ethernet gateway, so it was necessary to look for other options for work.


Looking at the available binders, I found universal Http binding and Exec binding , with the help of which it was possible to realize communication with NooLite:


  1. Http binding allows you to perform a network request when an event occurs (on / off, receiving information from the sensor). Using the API in PR1132 , you could implement OpenHab direct communication with NooLite.
  2. Exec binding - any command is executed for the event.

At that time, I thought that additional pre / post processing logic for requests to NooLite would be needed, so I chose the second option.


NooLite CLI


When developing a Telegram bot, a previous implementation of the interface for communicating with a smart home, I already wrote a simple wrapper class above the API calls to NooLite:


noolite_api.py
 """ NooLite API wrapper """ import requests from requests.auth import HTTPBasicAuth from requests.exceptions import ConnectTimeout, ConnectionError import xml.etree.ElementTree as ET class NooLiteSens: """    ,         """ def __init__(self, temperature, humidity, state): self.temperature = float(temperature.replace(',', '.')) if temperature != '-' else None self.humidity = int(humidity) if humidity != '-' else None self.state = state class NooLiteApi: """     NooLite""" def __init__(self, login, password, base_api_url, request_timeout=10): self.login = login self.password = password self.base_api_url = base_api_url self.request_timeout = request_timeout def get_sens_data(self): """   xml    :return:  NooLiteSens     :rtype: list """ response = self._send_request('{}/sens.xml'.format(self.base_api_url)) sens_states = { 0: ' ,   ', 1: '  ', 2: '   ', 3: '     ' } response_xml_root = ET.fromstring(response.text) sens_list = [] for sens_number in range(4): sens_list.append(NooLiteSens( response_xml_root.find('snst{}'.format(sens_number)).text, response_xml_root.find('snsh{}'.format(sens_number)).text, sens_states.get(int(response_xml_root.find('snt{}'.format(sens_number)).text)) )) return sens_list def send_command_to_channel(self, data): """   NooLite    NooLite  url   data :param data: url  :type data: dict :return: response """ return self._send_request('{}/api.htm'.format(self.base_api_url), params=data) def _send_request(self, url, **kwargs): """   NooLite        url    kwargs :param url: url   :type url: str :return: response  NooLite   """ try: response = requests.get(url, auth=HTTPBasicAuth(self.login, self.password), timeout=self.request_timeout, **kwargs) except ConnectTimeout as e: print(e) raise NooLiteConnectionTimeout('Connection timeout: {}'.format(self.request_timeout)) except ConnectionError as e: print(e) raise NooLiteConnectionError('Connection timeout: {}'.format(self.request_timeout)) if response.status_code != 200: raise NooLiteBadResponse('Bad response: {}'.format(response)) else: return response #   NooLiteConnectionTimeout = type('NooLiteConnectionTimeout', (Exception,), {}) NooLiteConnectionError = type('NooLiteConnectionError', (Exception,), {}) NooLiteBadResponse = type('NooLiteBadResponse', (Exception,), {}) NooLiteBadRequestMethod = type('NooLiteBadRequestMethod', (Exception,), {}) 

Using it, in a few lines of python code, I sketched the NooLite CLI , with the help of which the opportunity to manage NooLite from the command line appeared:


noolite_cli.py
 """ NooLite PR1132 command line interface """ import os import json import logging import argparse import yaml from noolite_api import NooLiteApi SCRIPT_PATH = os.path.dirname(os.path.realpath(__file__)) # Logging config logger = logging.getLogger() formatter = logging.Formatter( '%(asctime)s - %(filename)s:%(lineno)s - %(levelname)s - %(message)s' ) stream_handler = logging.StreamHandler() stream_handler.setFormatter(formatter) logger.addHandler(stream_handler) def get_args(): """   :return:   {: }   . :rtype: dict """ parser = argparse.ArgumentParser() parser.add_argument('-sns', type=int, help='    ') parser.add_argument('-ch', type=int, help=' ') parser.add_argument('-cmd', type=int, help='') parser.add_argument('-br', type=int, help=' ') parser.add_argument('-fmt', type=int, help='') parser.add_argument('-d0', type=int, help='  0') parser.add_argument('-d1', type=int, help='  1') parser.add_argument('-d2', type=int, help='  2') parser.add_argument('-d3', type=int, help='  3') return {key: value for key, value in vars(parser.parse_args()).items() if value is not None} if __name__ == '__main__': #     config = yaml.load(open(os.path.join(SCRIPT_PATH, 'conf_cli.yaml'))) #      NooLite noolite_api = NooLiteApi( config['noolite']['login'], config['noolite']['password'], config['noolite']['api_url'] ) #    args = get_args() logger.debug('Args: {}'.format(args)) #    sns,      if 'sns' in args: sens_list = noolite_api.get_sens_data() send_data = sens_list[args['sns']] print(json.dumps({ 'temperature': send_data.temperature, 'humidity': send_data.humidity, 'state': send_data.state, })) else: logger.info('Send command to noolite: {}'.format(args)) print(noolite_api.send_command_to_channel(args)) 

The arguments have the same names as in the API of the PR1132 ethernet gateway , the only thing added is the sns argument for receiving information from the sensors.


 #  $ python noolite_cli.py -h usage: noolite_cli.py [-h] [-sns SNS] [-ch CH] [-cmd CMD] [-br BR] [-fmt FMT] [-d0 D0] [-d1 D1] [-d2 D2] [-d3 D3] optional arguments: -h, --help show this help message and exit -sns SNS      -ch CH   -cmd CMD  -br BR   -fmt FMT  -d0 D0   0 -d1 D1   1 -d2 D2   2 -d3 D3   3 #   ,   0  $ python noolite_cli.py -ch 0 -cmd 2 OK #   ,   0  $ python noolite_cli.py -ch 0 -cmd 0 OK #    ,   0  $ python noolite_cli.py -sns 0 {"state": " ,   ", "temperature": 21.1, "humidity": 56} #  -  RGB- SD111-180 (3 )   #     : d0 - , d1 - , d2 -  $ python noolite_cli.py -ch 3 -cmd 6 -fmt 3 -d0 247 -d1 255 -d2 247 

Now I could describe all the NooLite power units in items:


 # /etc/openhab2/items/noolite.items Number FFTemperature " [%.1f °C]" <temperature> {exec="<[python noolite_cli.py -sns 0:5000:JSONPATH($.temperature)]"} Number FFHumidity " [%d %%]" <temperature> {exec="<[python noolite_cli.py -sns 0:5000:JSONPATH($.humidity)]"} Switch Heaters1 "" { exec=">[OFF:python noolite_cli.py -ch 0 -cmd 0] >[ON:python noolite_cli.py -ch 0 -cmd 2]"} Switch Light1 "" { exec=">[OFF:python noolite_cli.py -ch 2 -cmd 0] >[ON:python noolite_cli.py -ch 2 -cmd 2]"} Color RGBLight " " <slider> 

Where:



Rules


The LED strip control no longer fit into one command, as additional logic was needed to obtain the brightness values ​​of each of the RGB channels. Therefore, I described the handling of state changes in rules:


 # /etc/openhab2/rules/noolite.rules import org.openhab.core.library.types.* var HSBType hsbValue var String redValue var String greenValue var String blueValue rule "Set RGB value" when Item RGBLight changed then val hsbValue = RGBLight.state as HSBType val brightness = hsbValue.brightness.intValue val redValue = ((((hsbValue.red.intValue * 255) / 100) * brightness) / 100).toString val greenValue = ((((hsbValue.green.intValue * 255) / 100) * brightness) / 100).toString val blueValue = ((((hsbValue.blue.intValue *255) / 100) * brightness) / 100).toString var String cmd = "python noolite_cli.py -ch 3 -cmd 6 -fmt 3 -d0 " + redValue + " -d1 " + greenValue + " -d2 " + blueValue executeCommandLine(cmd) end 

I described one rule that worked when the state of RGBLight changed, where I got the values ​​of each channel (0-255), formed the string python noolite_cli.py -ch 3 -cmd 6 -fmt 3 -d0 redValue -d1 greenValue -d2 blueValue and executed it .


Sitemap


Now, when OpenHab "saw" all my power units and knew how to control them, it remained to describe how to display them in the OpenHab control panel (Basic UI, Classic UI). This is done in the sitemap:


 # /etc/openhab2/sitemap/noolite.sitemap sitemap noolite label="" { Frame label=" " { Text item=FFTemperature Text item=FFHumidity Switch item=Heaters1 Switch item=Light1 } Frame label=" " { Colorpicker item=RGBLight icon="colorwheel" label=" " } } 

After saving this file, all devices appeared in the Basic UI:


image


I also tested working with a smart home through the OpenHab application for iOS devices, where everything also worked fine:


image image





Install and configure HomeKit for OpenHab


I installed the HomeKit add-on for OpenHab in a couple of clicks through the Paper UI (administration interface):


image


For its correct operation, following the documentation, for each element of the items I registered the type (Lighting, Switchable, CurrentTemperature, CurrentHumidity, Thermostat). After that, the noolite.items file had the following form:


 Number FFTemperature " [%.1f °C]" <temperature> [ "CurrentTemperature" ] {exec="<[python noolite_cli.py -sns 0:5000:JSONPATH($.temperature)]"} Number FFHumidity " [%d %%]" <temperature> [ "CurrentHumidity" ] {exec="<[python noolite_cli.py -sns 0:5000:JSONPATH($.humidity)]"} Switch Heaters1 "" [ "Switchable" ] { exec=">[OFF:python noolite_cli.py -ch 0 -cmd 0] >[ON:python noolite_cli.py -ch 0 -cmd 2]"} Switch Light1 "" [ "Switchable" ] { exec=">[OFF:python noolite_cli.py -ch 2 -cmd 0] >[ON:python noolite_cli.py -ch 2 -cmd 2]"} Color RGBLight " " <slider> [ "Lighting" ] 

Then, in the add-on settings, I registered the local address of the device with OpenHab (in my case, Raspberry Pi) and looked at the pairing pin-code:


image


')

image

After that, the OpenHab settings were completed, and I proceeded to configure the smart home on the iphone in the application "Home".





Setting up the Home application


image
The first launch of the application "Home"


image
Clicked "Add Accessory"


image
iPhone saw a device on the local network with HomeKit support


image
Enter code manually


image
When adding the specified pin code from the settings of HomeKit addon


image
All devices


The iPhone saw all of my devices described in items. Next, I renamed some devices, created "rooms" (first floor, second floor, street) and scattered all the devices on them.


Here I want to clarify one point. In the screenshot above, you can see the element "Outdoor temperature" (the first element with a 2 degrees index), located in the "Street" room. This element is implemented using the YahooWeather Binding Binding - essentially just a yahoo weather forecast for a specific location.


image


It does not belong to NooLite, so I did not mention the details of its installation and configuration. This can be done again through Paper UI, all described in detail in the documentation .


Remote access and automation


In my local network was Apple TV, which, without additional settings, was itself defined as the "Home Center". As I later found out, the home center is needed for remote access to the smart home and automation settings (scheduled actions, actions based on your geo-location, etc.). Apple TV of the 3rd or 4th generation can act as a home center (in the 3rd generation only remote access works, 4th generation is necessary for automation) or an iPad with iOS 10. This device must be constantly turned on and located in the local network of the smart home.
It was pleasantly pleased that I did not make any additional settings with Apple TV. All you need is to log in to iCloud with your account.





Result


Most clearly the work process can be shown using video. Below are some examples of the work of the application "Home" and voice control via Siri:





In the dashboard 2 extreme right places are hired contactors, controlled by power units NooLite series SL. Through them are connected lines of heaters on the first and second floor of the house. On the video you can hear them click on / off. Unfortunately, there is no more visual indication of their work.


At the beginning of the next video, I disconnect from the dacha Wi-Fi network and all further work with the smart home takes place via 3G mobile Internet.








Conclusion


Integration with HomeKit allowed us to add to the smart home a convenient management interface with iOS devices through the Home application, and also expanded its functionality:



A detailed overview of the Home application itself and its applications for home automation is already beyond the scope of this topic and I think it deserves a separate article. But for me the most interesting is geolocation, on the basis of which you can implement interesting automation scenarios. For example, when all users at home leave it, you can turn off the light everywhere. If users have retired even further (left for the city), then turn off some consumers, for example, sockets and electricity in the basement (I have a pump station there).


The work on this article allowed to look at the country house management system from the side and note the places that will be refined in the near future:



References:


Source: https://habr.com/ru/post/312668/


All Articles