Domoticz on 30 Euro hardware - All in (for KaKu/Coco)

In this subforum you can show projects you have made, or you are busy with. Please create your own topic.
Post Reply
mjdb
Posts: 87
Joined: Thursday 12 January 2017 14:38
Target OS: Raspberry Pi
Domoticz version: all beta
Location: NL - Pijnacker
Contact:

Domoticz on 30 Euro hardware - All in (for KaKu/Coco)

Post by mjdb » Wednesday 02 August 2017 15:12

In the following posts I will describe how I created a working Domoticz on 30 Euro hardware, including 433 Mhz KaKu (Klik Aan Klik Uit) / CoCo (Click On Click Off) functionality.

The reason I came to this project is that in some 'corners' of my house the reception of 433 Mhz signals was bad. I already have two RfxTrx433's, but still some switches and lights were unreliable. So I was looking for a cheap alternative to create Domoticz 'satellites' that would fill in the gaps.

Domoticz runs on a Raspberri Pi Zero W (= Wifi) (11 Euro) with a micro SD-card (8 Euro). The communication is looked after by an Arduino Nano clone (3 Euro). The receiver is a RXB6 433 Mhz RF receiver (2,50 Euro). That is not a cheap one from a kit you see everywhere for a few Euro. The transmitters from those kits a quite fine. Otherwise you could use a HopeRF RFM85W transmitter (2,50 Euro). Since the Raspberry Pi works with 3,3 volt logic and the Arduino with 5 volt logic, they are connected through a level converter (1,50 Euro).
You can build it on a breadboard (3 Euro) or solder it on a development print-board (0,65 Euro). You will also need some connectors, wires, leds, resistors and capacitors, but all in all you can build it for about 30 Euro's.

This is what you make (breadboard):
Breadboard_Frontview.jpg
Breadboard_Frontview.jpg (60.39 KiB) Viewed 3918 times
Or compact on a development print-board:
Attachments
Printboard_WithLeds.jpg
Printboard_WithLeds.jpg (45.3 KiB) Viewed 3918 times
Last edited by mjdb on Wednesday 02 August 2017 17:55, edited 8 times in total.
- - - - - - - - - - -
2 x Domoticz on Raspberry Pi; 2 x RFXtrx433; Aeotec Z-Stick
KlikAanKlikUit ICS-2000 as Relay
Aeotec MultiSensors 6; Danfoss Z Thermostats 014G0013; Kaku Switches, Z-wave Switches

mjdb
Posts: 87
Joined: Thursday 12 January 2017 14:38
Target OS: Raspberry Pi
Domoticz version: all beta
Location: NL - Pijnacker
Contact:

Domoticz on 30 Euro hardware - Prepare the Raspberry Pi Zero W

Post by mjdb » Wednesday 02 August 2017 15:13

If you are reading this, you probably know how to use a Raspberry Pi and to install Domoticz.
Perhaps, the Raspberry Pi Zero W is a little bit different, so feel free to check:

Step 1. You can use a pre-installed NOOBS SD-card or prepare an empty SD card.

For creating a SD-card from scratch, use program 'ImageWriter'. As source I used the image '2017-03-02-raspbian-jessie.img'. Please see internet.

Step 2. Initialize the Raspberry Pi.

Connect the Raspberry Pi Zero W to a monitor and an USB hub. You will need a microUSB adapter. For the monitor you will need a mini HDMI to HDMI adapter.
RaspberrPiZeroW_connected.jpg
RaspberrPiZeroW_connected.jpg (45.73 KiB) Viewed 3916 times
Place the SD-card in the Raspberry Pi Zero W and power on.

Configure:
- Click on the Raspberry Icon --> Preferences --> Rasberry Pi Configuration
- Set your Password
- Set your interfaces --> SSH: Enabled
- Set your Language, Locale, Time zone, Keyboard and Wifi Country

You should presumably reboot.

Step 3. Network.

- Open a command shell

sudo iwlist wlan0 scan

- Look for your network in the list. Make a note of the name behind ESSID:
RaspPiNetwork-01-Scan.png
RaspPiNetwork-01-Scan.png (76.63 KiB) Viewed 3901 times
sudo nano /etc/wpa_supplicant/wpa_supplicant.conf

Go to the bottom of the file and add the following:

network={
ssid="<your network ID as found after ESSID>"
psk="<your network password"
}

- Save the file and reboot

- - - -

- On your monitor, look for the network-icon top right
--> Click Right --> Wireless & Wired Network Settings
--> Select your network
--> Network Preferences --> Interface --> wlan0
Fill the empty fields:
IP-adres --> 192.168.0.123 (choose a suitable IP-address in you IP-range)
Router --> 192.168.0.1 (The gateway from your network to internet)
DNS Servers --> 194.109.6.66 (The DNS server of your internet provider)
DNS Search --> 194.109.6.66 (The DNS server of your internet provider)
RaspPiNetwork-04-Preferences.png
RaspPiNetwork-04-Preferences.png (34.04 KiB) Viewed 3901 times
- Save and reboot

- - - -

- You should now have a working internet connection.

- The Raspberry Pi Zero W has a problem updating its date and time. We use an alternative:

- Open a command shell

sudo route add default gw 192.168.0.1 (use the IP-address of your gateway)

sudo apg-get install htpdate

- Maak deze instellingen permanent:

sudo nano /etc/rc.local

- before the line with 'exit 0' type

sleep 15

sudo route add default gw 10.0.1.1

sudo htpdate http://www.raspbian.com


Save (<Ctrl> <O>) and close nano (<Ctrl> <X>)

sudo reboot

Step 4. Update the Raspberry Pi.

- Open a command shell

sudo apt-get update

sudo apt-get dist-upgrade


- Remove unnecessary software (if you want to)
- Don't forget to answer Yes so now and then; it can take a long time.

sudo apt-get remove mathematica-fonts wolfram-engine scratch sonic-pi nodered oracle-java8-jdk

sudo apt-get remove minecraft-pi dillo gpicview penguinspuzzle oracle-java8-jdk openjdk-7-jre oracle-java7-jdk openjdk-8-jre

sudo apt-get remove libreoffice-writer libreoffice-calc libreoffice-base libreoffice-impress libreoffice-draw libreoffice-math libreoffice

sudo apt-get clean

sudo apt-get autoremove

sudo reboot


Step 5. Remote access

If you like to work 'remote' with your Raspberry PI, for example from your Windows PC, install software for Windows Remote Desktop:

- Open a command shell

sudo apt-get install vnc4server

sudo apt-get install xrdp

Try to connect from your desktop...

Step 6. Install Domoticz

sudo curl -L install.domoticz.com | sudo bash

- We will use GPIO pins, so make them available

sudo nano /boot/config.txt


- Go to the bottom of the file and add:

dtoverlay=w1-gpio

enable_uart=1


Save (<Ctrl> <O>) and close nano (<Ctrl> <X>)

sudo nano /boot/cmdline.txt

- look for the text "console=serial0,11520"
- remove this text and leve the part before it and the part behind it unchanged.

Save (<Ctrl> <O>) and close nano (<Ctrl> <X>)

- Make the necessary changes to the Domoticz startup-script:

sudo nano /etc/init.d/domoticz.sh

- look for the line '### END INIT INFO' (line 9)
- add the following text:

# for resetting the Arduino
# Physical Pin 12, GPIO 1, BCM 18
sudo sh -c 'echo 18 > /sys/class/gpio/export'
sudo sh -c 'echo high > /sys/class/gpio/gpio18/direction'
sudo sh -c 'echo 1 > /sys/class/gpio/gpio18/active_high'

# For showing ON status through a LED
# Physical Pin 16, GPIO 4, BCM 238
sudo sh -c 'echo 23 > /sys/class/gpio/export'
sudo sh -c 'echo high > /sys/class/gpio/gpio23/direction'
sudo sh -c 'echo 1 > /sys/class/gpio/gpio23/active_high'


Save (<Ctrl> <O>) and close nano (<Ctrl> <X>)

sudo reboot

Now you should have a working Domoticz system
Last edited by mjdb on Wednesday 02 August 2017 17:43, edited 13 times in total.
- - - - - - - - - - -
2 x Domoticz on Raspberry Pi; 2 x RFXtrx433; Aeotec Z-Stick
KlikAanKlikUit ICS-2000 as Relay
Aeotec MultiSensors 6; Danfoss Z Thermostats 014G0013; Kaku Switches, Z-wave Switches

mjdb
Posts: 87
Joined: Thursday 12 January 2017 14:38
Target OS: Raspberry Pi
Domoticz version: all beta
Location: NL - Pijnacker
Contact:

Domoticz on 30 Euro hardware - Prepare the Arduino Development Environment

Post by mjdb » Wednesday 02 August 2017 15:14

If you have worked with Arduino before you presumably have the IDE (Development Environment) on your desktop computer.
For this project we will install the Arduino IDE on the Raspberri Pi Zero W and we will program the Arduino over de Rx/Tx GPIO pins from the Raspberry Pi.

- Open a command shell

sudo apt-get install arduino

The Arduino program (Sketch) communicates with the 433 Mhz Receiver and Transmitter. Therefore we use a library 'NewRemoteReceiver'.

Prepare the folders:

sudo chmod -R 777 /usr/share/arduino
sudo mkdir /usr/share/arduino/downloads
sudo chmod -R 777 /usr/share/arduino/downloads
sudo chmod -R 777 /usr/share/arduino/libraries


In a browser, open:
https://bitbucket.org/fuzzillogic/433mh ... /wiki/Home

For typing, note: fuzzillogic is with two l's; Home is with large H.
RaspPiSetup-Arduino-3b.png
RaspPiSetup-Arduino-3b.png (258.55 KiB) Viewed 3899 times
- Download:

https://bitbucket.org/fuzzillogic/433mh ... stable.zip

- Open the downloaded zip-file
- Unpack to /usr/share/arduino/downloads
RaspPiSetup-Arduino-8.png
RaspPiSetup-Arduino-8.png (30.65 KiB) Viewed 3899 times
- with explorer, go to /usr/share/arduino/downloads
- open folder fuzillogic-433mhzforarduino-e8eff1271a01
- click right on folder NewRemoteSwitch (don't open) --> choose copy
- go to folder /usr/share/arduino/libraries
- right click --> paste

See if you can find the Arduino IDE:
- Click Raspberry Icon --> Programming --> Arduino
RaspPiSetup-Arduino-11.png
RaspPiSetup-Arduino-11.png (32.07 KiB) Viewed 3899 times
- Power Down
Last edited by mjdb on Wednesday 02 August 2017 15:46, edited 3 times in total.
- - - - - - - - - - -
2 x Domoticz on Raspberry Pi; 2 x RFXtrx433; Aeotec Z-Stick
KlikAanKlikUit ICS-2000 as Relay
Aeotec MultiSensors 6; Danfoss Z Thermostats 014G0013; Kaku Switches, Z-wave Switches

mjdb
Posts: 87
Joined: Thursday 12 January 2017 14:38
Target OS: Raspberry Pi
Domoticz version: all beta
Location: NL - Pijnacker
Contact:

Domoticz on 30 Euro hardware - Build the hardware

Post by mjdb » Wednesday 02 August 2017 15:14

So you need:
- 1 x Raspberry Pi Zero W
- 1 x 20 pin 90 degrees connector
- 1 x 20 pin 90 degrees connector with long legs (or straight long pins)
- 1 x Arduino Nano (clone)
- 1 x Level converter 4 channel
- 1 x 433 Mhz Receiver
- 1 x 433 Mhz Transmitter
- 2 x Capacitor 100 nano Farrad
- 2 x resistor (about) 1 kilo Ohm
- 1 led Red
- 1 led Blue

I suppose you will start on a breadboard.

This is your scheme:
BreadBoard_Scheme1.png
BreadBoard_Scheme1.png (180.36 KiB) Viewed 3915 times
And this is what you should get:
Breadboard_Topview.JPG
Breadboard_Topview.JPG (51.47 KiB) Viewed 3915 times
And you could even try powering it on. After a short period the blue led should be lit to indicate that the Raspberry Pi is active.
Last edited by mjdb on Wednesday 02 August 2017 15:26, edited 2 times in total.
- - - - - - - - - - -
2 x Domoticz on Raspberry Pi; 2 x RFXtrx433; Aeotec Z-Stick
KlikAanKlikUit ICS-2000 as Relay
Aeotec MultiSensors 6; Danfoss Z Thermostats 014G0013; Kaku Switches, Z-wave Switches

mjdb
Posts: 87
Joined: Thursday 12 January 2017 14:38
Target OS: Raspberry Pi
Domoticz version: all beta
Location: NL - Pijnacker
Contact:

Domoticz on 30 Euro hardware - Working with the Arduino Development Environment

Post by mjdb » Wednesday 02 August 2017 15:14

- Open the Arduino IDE
- Click Raspberry Icon --> Programming --> Arduino

- Extra --> Board --> Arduino Nano w/ ATmega328
- Extra --> Serial Port --> /dev/ttyS0
- Extra --> Programmer --> AVRISP mkll
- Extra --> Serial Monitor --> Speed --> 115200 baud

When you upload your sketch to the Arduino, it should be reset exactly at the moment that the uploading starts. However, you can do that from your Raspberry Pi. Therefore we use GPIO 1 (pin 12) that is connected (through the level converter) to the Reset pin from the Arduino.

- Open a command shell

gpio write 1 0 (You should see the red led turn on)

gpio write 1 1 (Now the red led should be off)

If this works, you have reset the Arduino.

For testing, open sketch 'ShowReceivedCode' in
~ arduino/libraries/NewRemoteSwitch/examples/ShowReceivedCode

- In your command shell type: gpio write 1 0 (You should see the red led turn on)
- Now prepare text: gpio write 1 1 (DO NOT ENTER)

- Close the Serial Monitor window.

- In Arduino IDE, choose upload (<Ctrl> + U)
- Then select the command shell again and be prepared...

- Immediately when in the Arduino IDE the text "Binairy sketch-size..." is shown:

press <ENTER>

This should be done between 0.1 and 0.3 seconds after you see the message "Binairy sketch-size...".

- Now, the upload should be running. You will see a message when it was succesful.
- If not (you might see some lines about some avrdude that is not responding): try again and again and again...

If you can't get this working, verify the Board, the Serial Port, the Programmer and the speed of the Serial Monitor (115.200 baud). Then Close the Serial Monitor.

If it still doesn't work, use a USB cable.

When the upload worked, open the arduino IDE serial monitor.

Press some KaKu/CoCo switches. Their codes should be shown in the monitor-window.
Last edited by mjdb on Wednesday 02 August 2017 17:53, edited 2 times in total.
- - - - - - - - - - -
2 x Domoticz on Raspberry Pi; 2 x RFXtrx433; Aeotec Z-Stick
KlikAanKlikUit ICS-2000 as Relay
Aeotec MultiSensors 6; Danfoss Z Thermostats 014G0013; Kaku Switches, Z-wave Switches

mjdb
Posts: 87
Joined: Thursday 12 January 2017 14:38
Target OS: Raspberry Pi
Domoticz version: all beta
Location: NL - Pijnacker
Contact:

Domoticz on 30 Euro hardware - Have Arduino monitor the 433 Mhz ether

Post by mjdb » Wednesday 02 August 2017 15:15

So, when you have a working Raspberry PI and a working Arduino with 433 Mhz Communication we will install the monitor program/sketch on the Arduino.

This is the program/sketch 'DomoticzFrontEnd':

Code: Select all

// DomoticzFrontEnd

#include <NewRemoteReceiver.h>
#include <NewRemoteTransmitter.h>

const int StatePin = 13;
unsigned long KaKuNum = 0; // will contain the numerical KaKu ID
String KaKuHex = "12345678"; // will contain the Hex KaKu ID
int KaKuUnit = 0; // will contain the number of the selected unit
int KaKuState = 0; // the On/Off state of the KaKu Device

String RaspPiText = ""; // messages from Raspberry Pi to Arduino

// Function KaKuHexToNum translates the hex-address of a
// KaKu-device to the numerical equivalent.
unsigned long KaKuHexToNum(String HexInput) {

  String HexChars = "0123456789ABCDEF";
  unsigned long result = 0;
  for (int i = 0; i<7; i++) {
    result = result * 16 + HexChars.indexOf(HexInput.substring(i, i+1));
  }

  return result;

} // KaKuHexToNum

// Function KaKuNumToHex translates the numerical address
// of a KaKu-device to the hex equivalent (as String)
String KaKuNumToHex(unsigned long NumInput) {

  String HexChars = "0123456789ABCDEF";
  String Output = "";
  unsigned long Remainder = NumInput;
  unsigned long IntegerPart = 0;
  int DecimalPart = 0;

  while (Remainder > 0) {
    IntegerPart = Remainder / 16; // should leave the decimals away
    DecimalPart = Remainder - IntegerPart * 16;
    Remainder = IntegerPart;
    Output = HexChars.substring(DecimalPart, DecimalPart+1) + Output;
  }

  while (Output.length() < 7) {
    // extend to 7 characters with zero-prefixes
    Output = "0" + Output;
  }

  return Output;

} // KaKuNumToHex

// function KaKuTransmit sends an On or Off command to the transmitter
void KaKuTransmit(unsigned long SendAddress, int SendUnit, bool SendState) {

  // switch off the receiver to avoid loops
  NewRemoteReceiver::disable();

  // initialize a transmitter-session for this device
  // = Device-address, Arduino-pin, duration, repeat-factor (2^3 = 8)
  NewRemoteTransmitter transmitter(SendAddress, 3, 260, 3);

  // send the New state to the required Unit
  transmitter.sendUnit(SendUnit, SendState);

  // re-enable the receiver after sending
  NewRemoteReceiver::enable();

} // KaKuTransmit

// function ReceiveFromRaspPi receives the address, unit
// and NewState that should be transmitted
String ReceiveFromRaspPi() {

  char EndOfLine = '\n'; // indicates the end of a message
  String ReceivedString = "";

  ReceivedString = Serial.readStringUntil(EndOfLine);
  return ReceivedString;

} // ReceiveFromRaspPi

void setup() {

  delay(100);
  Serial.begin(115200);
  delay(100);
  Serial.println("setup...");
  delay(100);

  pinMode(StatePin, OUTPUT);
  digitalWrite(StatePin, LOW);

  // initiates (background) waiting for incoming KaKu-signals
  NewRemoteReceiver::init(0, 2, ProcessKaKuCode);

  for (int i = 0; i<3; i++) {
    digitalWrite(StatePin, HIGH);
    delay(300);
    digitalWrite(StatePin, LOW);
    delay(300);
  }

  Serial.println("setup done.");
  delay(100);

} // setup

void loop() {

  String Temp = "";

  // if a KaKu-signal was received via function ProcessKaKuCode
  // KaKuNum will contain the numerical ID-value of the Device.
  if (KaKuNum > 0) {

    // the ID is presented in Hex notation
    KaKuHex = KaKuNumToHex(KaKuNum);

    // this goes to a Python program on the Raspberry Pi
    digitalWrite(StatePin, HIGH);
    Serial.print("KaKu ");
    Serial.print(KaKuHex);
    Serial.print("-");
    Serial.print(KaKuUnit+1); // the presentation starts from 1
    Serial.print(":");
    Serial.print(KaKuState);
    Serial.println("");
    Serial.flush();
    digitalWrite(StatePin, LOW);

    // after processing, clear for next round
    KaKuHex = "";
    KaKuNum = 0;
    KaKuUnit = 0;
    KaKuState = 0;

  } // if KaKuNum > 0

  // see if the Raspberry Pi has a sending-task waiting
  if (Serial.available() > 0) {

    // Receive the complete message from raspberry Pi
    RaspPiText = ReceiveFromRaspPi();

    digitalWrite(StatePin, HIGH);
    Serial.print("Echo: "); // respond
    Serial.println(RaspPiText);

    if (RaspPiText.substring(0, 4) == "KaKu") {

      // Split the message in relevant parts
      KaKuHex = RaspPiText.substring(RaspPiText.indexOf(" ")+1, RaspPiText.indexOf("-"));
      Temp = RaspPiText.substring(RaspPiText.indexOf("-")+1, RaspPiText.indexOf(":"));
      KaKuUnit = Temp.toInt()-1;
      Temp = RaspPiText.substring(RaspPiText.indexOf(":")+1);
      KaKuState = Temp.toInt();
      KaKuNum = KaKuHexToNum(KaKuHex);

      // Send the new state to the KaKu-device
      KaKuTransmit(KaKuNum, KaKuUnit, KaKuState);

      // after processing, clear data for next round
      KaKuHex = "";
      KaKuNum = 0;
      KaKuUnit = 0;
      KaKuState = 0;
    } // A KaKu command was received

    digitalWrite(StatePin, LOW);

  } // if there is something in the serial buffer

  // a small delay to avoid continous 100% processor
  delay(50);

} // loop

// This function is called when a KaKu-command was received over 433 Mhz
void ProcessKaKuCode(NewRemoteCode receivedCode) {

  // Save the received data to process in the loop-process
  KaKuNum = receivedCode.address;
  KaKuUnit = receivedCode.unit;
  KaKuState = receivedCode.switchType;

} // ProcessKaKuCode
Copy DomoticzFrontEnd to the Raspberry Pi (I use FTP from my desktop) to
/home/pi/sketchbook/DomoticzFrontEnd/DomoticzFrontEnd.ino

Open the Arduino IDE on your Raspberry Pi and open the sketch DomoticzFrontEnd.

Upload the sketch to the Arduino as described in the previous post.

Whene the Sketch is running, it blinks the Arduino system Led three times.

You could open the Serial Monitor and then reset the Arduino.
You will see 'setup...' and 'setup done' messages.
Last edited by mjdb on Wednesday 02 August 2017 17:52, edited 4 times in total.
- - - - - - - - - - -
2 x Domoticz on Raspberry Pi; 2 x RFXtrx433; Aeotec Z-Stick
KlikAanKlikUit ICS-2000 as Relay
Aeotec MultiSensors 6; Danfoss Z Thermostats 014G0013; Kaku Switches, Z-wave Switches

mjdb
Posts: 87
Joined: Thursday 12 January 2017 14:38
Target OS: Raspberry Pi
Domoticz version: all beta
Location: NL - Pijnacker
Contact:

Domoticz on 30 Euro hardware - Let Python connect the Arduino and Domoticz

Post by mjdb » Wednesday 02 August 2017 15:15

The 'Main' program to connect Domoticz and Arduino on the Raspberry Pi is a Python program. I called it ArdToDom.

This is Python program ArdToDom:

Code: Select all

#!/usr/bin/python3.4

# /home/pi/python_programs/ArdToDom.py
# ArdToDom = Arduino To Domoticz

# open: lxterminal -e "/home/pi/python_programs/ArdToDom.py"

import time
import RPi.GPIO as GPIO
import serial
import urllib.parse
import urllib.request
import datetime

ProgName = '(ArdToDom) '

# ArdToDom is used as an assistant to Domoticz
# to use basic and cheap 433Mhz receivers and transmitters
# to communicate with KaKu (Klik aan KliK uit) Devices,
# also known as CoCo (Click on Click off).

# Besides a Raspberry Pi ArdToDom uses an Arduino micro-computer,
# a 433Mhz Receiver and a 433Mhz Transmitter.
# ArdToDom runs on a Raspberry Pi Zero W (W = Wifi) and an
# Arduino Nano (or clone).
# Do NOT use such a very cheap 433 Mhz receiver/transmitter
# package but buy GOOD ones (consult your supplier).

# ArdToDom ONLY processes KaKu/CoCo commands (and
# of course 100% clones). Only the more recent KaKu/CoCo
# devices are recognized (with an A in the type).
# Older Devices where you have to select a group
# (like A to H) with a scroll-wheel are not recognized,
# nor are any other devices on the 433 Mhz band.

# ArdToDom communicates with the Arduino through
# a serial connection (Rx Tx).

SerialToArduino = serial.Serial()

# ArdToDom also communicates with the Domoticz instance
# on the Raspberry Pi where ArdToDom is running.
# using http / Json-requests.
# You can connect to a different Domoticz instance
# by changing the IP-address in variable 'DomoticzIP'.

DomoticzIP = 'http://127.0.0.1:8080'

# The Devices-fields (see GetDeviceList) are saved in the following list:

DeviceList = []
DeviceCount = 0

# Relevant entities in Domoticz will be called by Idx:

VirtualHardwareIdx = '' # See GetVirtualHardwareIdx
SwitchRequestName = 'SwitchRequest'
SwitchRequestIdx = '' # See GetVariableIdx
ArdToDomStateName = 'ArdToDomState'
ArdToDomStateIdx = ''

# Commands to and from Arduino are remembered to avoid double processing

LastArduinoCmds = []
LastArduinoCmdTime = 0 # time of last transmitted command

# There are various ways that ArdToDom should stop.
# Processes can set the 'StopPending' variable.
# This will cause the main-loop to abort.

StopPending = 0

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

# Function definitions:

def PrintLog(LogText):

    # PrintLog prints a LogText both to the Python-shell
    # as to the Domoticz Log-file

    print(LogText)

    # /json.htm?type=command&param=addlogmessage&message=MESSAGE

    UrlCmd = DomoticzIP + '/json.htm'
    UrlCmd = UrlCmd + '?type=command'
    UrlCmd = UrlCmd + '&param=addlogmessage'
    UrlCmd = UrlCmd + '&message='
    UrlCmd = UrlCmd + urllib.parse.quote(ProgName + LogText)
    UrlResult=urllib.request.urlopen(UrlCmd)

# end PrintLog


def Initialize():

    # The program is initialized

    global VirtualHardwareIdx
    global SwitchRequestIdx
    global ArdToDomStateIdx
    global StopPending

    PrintLog('Initialize...')

    PrintLog('')
    PrintLog('Configure serial to Arduino...')
    SerialToArduino.baudrate = 115200
    SerialToArduino.port = '/dev/ttyS0'
    SerialToArduino.timeout=3600
    SerialToArduino.open()
    SerialToArduino.flushOutput()
    SerialToArduino.flushInput()
    PrintLog(SerialToArduino.name)

    PrintLog('')
    ResetArduino()
    SendToArduino('I Am Alive')
    GetFromArduino(3) # Wait 3 seconds for reply

    # Domoticz can prepare a request to send a Switch-command
    # to a 433 Mhz Device to be send (by ArdToDom) to the Arduino.
    # Such a Switch-request is placed by a Domoticz-LUA-script
    # in a User Variable with the name SwitchRequestName.
    # The Value of the variable ' SwitchRequest' can be retrieved
    # by its Idx. Therefore, we must first find that Idx.

    PrintLog('')
    SwitchRequestIdx = GetVariableIdx(SwitchRequestName)
    # if not found, create the variable
    if (SwitchRequestIdx == ''):
        SwitchRequestIdx = CreateVariable(SwitchRequestName)
    PrintLog('SwitchRequestIdx <-- ' + SwitchRequestIdx)
    PendingRequest = ReadVariable(SwitchRequestIdx)
    WriteVariable(SwitchRequestName, 'stop') # make other instances stop

    PrintLog('')
    ArdToDomStateIdx = GetVariableIdx(ArdToDomStateName)
    # if not found, create the variable
    if (ArdToDomStateIdx == ''):
        ArdToDomStateIdx = CreateVariable(ArdToDomStateName)
    PrintLog('ArdToDomStateIdx <-- ' + ArdToDomStateIdx)

    PrintLog('')
    VirtualHardwareIdx = GetVirtualHardwareIdx()
    # if not found, create the Hardware
    if (VirtualHardwareIdx == ''):
        VirtualHardwareIdx = CreateVirtualHardware()
    PrintLog('VirtualHardwareIdx <-- ' + VirtualHardwareIdx)

    PrintLog('')
    CreateEventScript()

    WriteVariable(ArdToDomStateName, 'Get Device List')
    PrintLog('')
    DeviceCount = GetDeviceList()
    PrintLog('DeviceCount <-- ' + str(DeviceCount))

    WriteVariable(ArdToDomStateName, 'Initialize done')
    if (PendingRequest != 'stop'):
        WriteVariable(SwitchRequestName, PendingRequest) # restore Pending Request
    PrintLog('')
    PrintLog('Initialize done.')

# end Initialize


def GetVirtualHardwareIdx():

    # This program creates new (virtual) Switch/Light Devices.
    # So, they will have no hardware-device as owner.

    # Domoticz has to have 'Dummy' Hardware for virtual these
    # devices enabled. The idx of the Dummy Hardware is
    # placed in variable VirtualHardwareIdx.

    PrintLog('GetVirtualHardwareIdx...')

    # Like: /json.htm?type=hardware

    global StopPending

    CurrentName = ''
    CurrentType = 0
    CurrentIdx = ''
    FoundIdx = ''

    UrlCmd = DomoticzIP + '/json.htm'
    UrlCmd = UrlCmd + '?type=hardware'
    UrlResult=urllib.request.urlopen(UrlCmd)

    for UrlLines in UrlResult.readlines():
        CurrentLine = UrlLines.decode()

        # Scan all lines for keywords Name and idx
        for KeyWord in ['"Name"', '"idx"', '"Type"']:
            KeyWordLen = len(KeyWord)
            KeyWordPos = CurrentLine.find(KeyWord, 0)
            if (KeyWordPos > 0):
                # this is a line with a relevant keyword
                ValueStart = CurrentLine.find(':', KeyWordPos + KeyWordLen + 1) + 1
                ValueEnd = CurrentLine.find(',', ValueStart)
                if (ValueEnd == 0):
                    ValueEnd = len(CurrentLine)
                Value = CurrentLine[ValueStart:ValueEnd]
                Value = Value.replace('"', '')
                Value = Value.strip()

                if (KeyWord == '"Name"'):
                    CurrentName = Value

                if (KeyWord == '"Type"'):
                    CurrentType = Value

                if (KeyWord == '"idx"'):
                    CurrentIdx = Value

                    # When the idx is received, all fields are available
                    if (CurrentType == '15'): # Type = Virtual/Dummy hardware
                        FoundIdx = CurrentIdx
                        break

                    # clear the fields for the next device
                    CurrentName = ''
                    CurrentType = 0
                    CurrentIdx = ''

                # end process each keyword
            # end if keyword was in line
        # end for KeyWord

    if (FoundIdx == ''):
        PrintLog('GetVirtualHardwareIdx: Domoticz Virtual Hardware Not Found')

    return FoundIdx

# GetVirtualHardwareIdx


def CreateVirtualHardware():

    # Create CreateVirtualHardware in Domoticz

    global StopPending

    PrintLog('CreateVirtualHardware...')

    NewIdx = ''

    UrlCmd = DomoticzIP + '/json.htm'
    UrlCmd = UrlCmd + '?type=command'
    UrlCmd = UrlCmd + '&param=addhardware'
    UrlCmd = UrlCmd + '&htype=15' # virtual hardware
    UrlCmd = UrlCmd + '&name=Dummy' # the name of the hardware
    UrlCmd = UrlCmd + '&enabled=true'
    UrlCmd = UrlCmd + '&datatimeout=0'

    UrlResult=urllib.request.urlopen(UrlCmd)
    for UrlLine in UrlResult.readlines():
        UrlLine = UrlLine.decode('utf-8')
        if (UrlLine.find("OK", 0) > 0):
            PrintLog('CreateVirtualHardware <-- OK')
            NewIdx = GetVirtualHardwareIdx()
            break
        if (UrlLine.find("ERR", 0) > 0):
            PrintLog('CreateVirtualHardware <-- ERROR')
            StopPending = 1
            break
    # end process result from urlopen

    return NewIdx

# CreateVirtualHardware


def GetVariableIdx(VariableName):

    # Like: //json.htm?type=command&param=getuservariables

    PrintLog('GetVariableIdx ' + VariableName + '...')

    global StopPending

    CurrentName = ''
    CurrentIdx = ''
    FoundIdx = ''

    UrlCmd = DomoticzIP + '/json.htm'
    UrlCmd = UrlCmd + '?type=command'
    UrlCmd = UrlCmd + '&param=getuservariables'
    UrlResult=urllib.request.urlopen(UrlCmd)

    for UrlLines in UrlResult.readlines():
        CurrentLine = UrlLines.decode()

        # Scan all lines for keywords Name and idx
        for KeyWord in ['"Name"', '"idx"']:
            KeyWordLen = len(KeyWord)
            KeyWordPos = CurrentLine.find(KeyWord, 0)
            if (KeyWordPos > 0):
                # this is a line with a relevant keyword
                ValueStart = CurrentLine.find(':', KeyWordPos + KeyWordLen + 1) + 1
                ValueEnd = CurrentLine.find(',', ValueStart)
                if (ValueEnd == 0):
                    ValueEnd = len(CurrentLine)
                Value = CurrentLine[ValueStart:ValueEnd]
                Value = Value.replace('"', '')
                Value = Value.strip()

                if (KeyWord == '"Name"'):
                    CurrentName = Value

                if (KeyWord == '"idx"'):
                    CurrentIdx = Value

                    # When the idx is received, all fields are available
                    if (CurrentName == VariableName):
                        FoundIdx = CurrentIdx
                        break

                    # clear the fields for the next variable
                    CurrentName = ''
                    CurrentIdx = ''

                # process each keyword
            # if keyword was in line
        # KeyWords

    if (FoundIdx == ''):
        PrintLog('GetVariableIdx: Variable SwitchRequest Not Found')

    return FoundIdx

# GetVariableIdx


def CreateVariable(VariableName):

    # Create a variable in Domoticz

    global StopPending

    PrintLog('CreateVariable ' + VariableName + '...')

    NewIdx = ''

    UrlCmd = DomoticzIP + '/json.htm'
    UrlCmd = UrlCmd + '?type=command'
    UrlCmd = UrlCmd + '&param=saveuservariable'
    UrlCmd = UrlCmd + '&vname=' + VariableName
    UrlCmd = UrlCmd + '&vtype=2' # string variable
    UrlCmd = UrlCmd + '&vvalue=' # we start empty

    UrlResult=urllib.request.urlopen(UrlCmd)
    for UrlLine in UrlResult.readlines():
        UrlLine = UrlLine.decode('utf-8')
        if (UrlLine.find("OK", 0) > 0):
            PrintLog('CreateVariable <-- OK')
            NewIdx = GetVariableIdx(VariableName)
            break
        if (UrlLine.find("ERR", 0) > 0):
            PrintLog('CreateVariable <-- ERROR')
            StopPending = 1
            break
    # end process result from urlopen

    return NewIdx

# CreateVariable


def ReadVariable(VariableIdx):

    global StopPending

    # The (Return) Value from the variable will contain the content
    Value = ''

    # like: /json.htm?type=command&param=getuservariable&idx=1

    UrlCmd = DomoticzIP + '/json.htm'
    UrlCmd = UrlCmd + '?type=command'
    UrlCmd = UrlCmd + '&param=getuservariable'
    UrlCmd = UrlCmd + '&idx=' + VariableIdx
    UrlResult=urllib.request.urlopen(UrlCmd)

    for UrlLine in UrlResult.readlines():
        CurrentLine = UrlLine.decode()
        if (CurrentLine.find("ERR", 0) > 0):
            PrintLog('ReadVariable Idx=' + str(VariableIdx) + ' <-- ERROR')
            StopPending = 1
            break
        KeyWordPos = CurrentLine.find("Value" , 0)
        if (KeyWordPos > 0):
            ValueStart = CurrentLine.find('"', KeyWordPos + 8) + 1
            ValueEnd = CurrentLine.find(',', ValueStart)
            if (ValueEnd == 0):
                ValueEnd = len(CurrentLine)
            Value = CurrentLine[ValueStart:ValueEnd]
            Value = Value.replace('"', '')
            Value = Value.strip()

    if (Value > ''):
        PrintLog('ReadVariable Idx=' + str(VariableIdx) + ' <-- '+ Value)

    return Value

# ReadVariable


def WriteVariable(VariableName, NewText):

    global StopPending

    # Write a new value to a Domoticz Variable

    UrlCmd = DomoticzIP + '/json.htm'
    UrlCmd = UrlCmd + '?type=command'
    UrlCmd = UrlCmd + '&param=updateuservariable'
    UrlCmd = UrlCmd + '&vname=' + VariableName # variable name
    UrlCmd = UrlCmd + '&vtype=2' # string-type
    UrlCmd = UrlCmd + '&vvalue=' + urllib.parse.quote(NewText)

    # Send the command
    UrlResult=urllib.request.urlopen(UrlCmd)

    # and check the result
    for UrlLine in UrlResult.readlines():
        UrlLine = UrlLine.decode()
        if (UrlLine.find("OK", 0) > 0):
            # PrintLog('WriteVariable <-- OK')
            break
        if (UrlLine.find("ERR", 0) > 0):
            PrintLog(UrlCmd)
            PrintLog('WriteVariable ' + VariableName + ' <-- ERROR')
            StopPending = 1
            break
    # process result from urlopen

# WriteVariable


def CreateEventScript():

    # Create the script in Domoticz that will make
    # the Switch Requests for this program.
    # The script will be named 'SetSwitchRequest'
    # It is an Event script that will be activated each time a Device
    # in Domoticz is changed.

    PrintLog('CreateEventScript...')

    EventScriptFilePath = '/home/pi/domoticz/scripts/lua'
    EventScriptFileName = EventScriptFilePath + '/script_device_SetSwitchRequest.lua'
    fh = open(EventScriptFileName, 'w') #EventScriptFileHandle

    fh.write("-- SetSwitchRequest\n")
    fh.write("\n")
    fh.write("commandArray = {}\n")
    fh.write("\n")
    fh.write("  local ScriptName = '(SetSwitchRequest) '\n")
    fh.write("  local LastSensorName = tostring(next(devicechanged))\n")
    fh.write("  local LastSensorState = tostring(otherdevices[LastSensorName])\n")
    fh.write("  local LastSensorIdx = tostring(otherdevices_idx[LastSensorName])\n")
    fh.write("\n")
    fh.write("  function SecondsSince(s)\n")
    fh.write("    year = string.sub(s, 1, 4)\n")
    fh.write("    month = string.sub(s, 6, 7)\n")
    fh.write("    day = string.sub(s, 9, 10)\n")
    fh.write("    hour = string.sub(s, 12, 13)\n")
    fh.write("    minutes = string.sub(s, 15, 16)\n")
    fh.write("    seconds = string.sub(s, 18, 19)\n")
    fh.write("    t1 = os.time()\n")
    fh.write("    t2 = os.time{year=year, month=month, day=day, hour=hour, min=minutes, sec=seconds}\n")
    fh.write("   difference = os.difftime (t1, t2)\n")
    fh.write("    return tonumber(difference)\n")
    fh.write("  end -- SecondsSince\n")
    fh.write("\n")
    fh.write("if (SecondsSince(uservariables_lastupdate['" + ArdToDomStateName + "']) > 60) then\n")
    fh.write("  print('Start Python program ArdToDom...')\n")
    fh.write("  commandArray['Variable:" + ArdToDomStateName + "'] = 'Start pending'\n")
    fh.write("  Cmd = '\"/home/pi/python_programs/ArdToDom.py\"'\n")
    fh.write("  CmdResult = os.execute(Cmd .. ' &')\n")
    fh.write("  print(ScriptName .. 'Result Start ArdToDom <-- ' .. tostring(CmdResult))\n")
    fh.write("end\n")
    fh.write("\n")
    fh.write("  if ((LastSensorState == 'On') or (LastSensorState == 'Off')) then\n")
    fh.write("    print(ScriptName .. LastSensorIdx .. ': ' .. LastSensorState)\n")
    fh.write("    commandArray['Variable:" + SwitchRequestName + "'] = LastSensorIdx .. ': ' .. LastSensorState\n")
    fh.write("  end\n")
    fh.write("\n")
    fh.write("return commandArray\n")
    fh.write("\n")

    fh.close()

# CreateEventScript


def GetDeviceList():
    # To communicate with Domoticz, ArdToDom has to know
    # what (Light/Switch) devices are already known in Domoticz.
    # At start the Domoticz database is queried and the result
    # is loaded through function GetDeviceList(). The Devicelist will
    # contain the Device-ID, the Device-Unit, the Device-idx.

    CurrentID = ''
    CurrentIdx = ''
    CurrentName = ''
    CurrentUnit = ''
    ItemsFound = 0

    # Ask Domoticz for existing switches and lights
    PrintLog('GetDeviceList...')

    UrlCmd = DomoticzIP + '/json.htm'
    UrlCmd = UrlCmd + '?type=devices'
    UrlCmd = UrlCmd + '&filter=light'
    UrlResult=urllib.request.urlopen(UrlCmd)

    # Read all switches and lights from Domoticz
    for UrlLines in UrlResult.readlines():
        CurrentLine = UrlLines.decode()

        # Scan all lines for keywords ID, Unit and idx
        for KeyWord in ['"ID"', '"Unit"', '"Name"', '"idx"']:
            KeyWordLen = len(KeyWord)
            KeyWordPos = CurrentLine.find(KeyWord, 0)
            if (KeyWordPos > 0):
                # this is a line with a relevant keyword
                ValueStart = CurrentLine.find(':', KeyWordPos + KeyWordLen + 1) + 1
                ValueEnd = CurrentLine.find(',', ValueStart)
                if (ValueEnd == 0):
                    ValueEnd = len(CurrentLine)
                Value = CurrentLine[ValueStart:ValueEnd]
                Value = Value.replace('"', '')
                Value = Value.strip()

                if (KeyWord == '"ID"'):
                    CurrentID = Value

                if (KeyWord == '"Unit"'):
                    CurrentUnit = Value

                if (KeyWord == '"Name"'):
                    CurrentName = Value

                if (KeyWord == '"idx"'):
                    CurrentIdx = Value

                    # When the idx is received, all fields are available
                    # So, add the device to the list
                    DeviceList.append([CurrentID, CurrentUnit, CurrentIdx, CurrentName])
                    ItemsFound = ItemsFound + 1

                    # clear the fields for the next device
                    CurrentID = ''
                    CurrentUnit = ''
                    CurrentName = ''
                    CurrentIdx = ''

                # end process each keyword
            # end if keyword was in line
        # end for KeyWord

    return ItemsFound

# GetDeviceList


def SearchDeviceIdx(SearchID, SearchUnit):

    # Search for Device in list
    PrintLog('SearchDeviceIdx --> ' + SearchID + '-' + SearchUnit + '...')
    FoundIdx = '' # no index found yet

    for Device in DeviceList:
        if ((Device[0] == SearchID)
        and (Device[1] == SearchUnit)):
            FoundIdx = Device[2]
            break

    if (FoundIdx == ''):
        PrintLog('SearchDeviceIdx <-- Not found.')
    else:
        PrintLog('SearchDeviceIdx <-- Idx=' + FoundIdx + ' (' + Device[3] +')')

    return FoundIdx

# SearchDeviceIdx


def CreateDevice(NewID, NewUnit):

    # Create new device in Domoticz

    # ArdToDom receives from the Arduino ID's and
    # Unit-numbers from KaKu switches and lights.
    # Unknown ID's and Units are then Newly created in
    # Domoticz as virtual devices.

    global StopPending

    PrintLog('CreateDevice: ' + NewID + ' ' + NewUnit + '...')

    NewIdx = ''

    UrlCmd = DomoticzIP + '/json.htm'
    UrlCmd = UrlCmd + '?type=command'
    UrlCmd = UrlCmd + '&param=addswitch'
    UrlCmd = UrlCmd + '&hwdid=' + VirtualHardwareIdx
    UrlCmd = UrlCmd + '&name=' + 'ID=' + NewID + '%20Unit=' + NewUnit
    UrlCmd = UrlCmd + '&description=undefined'
    UrlCmd = UrlCmd + '&switchtype=0'
    UrlCmd = UrlCmd + '&lighttype=20'
    UrlCmd = UrlCmd + '&id=' + NewID
    UrlCmd = UrlCmd + '&unitcode=' + NewUnit

    UrlResult=urllib.request.urlopen(UrlCmd)
    for UrlLine in UrlResult.readlines():
        UrlLine = UrlLine.decode('utf-8')
        if (UrlLine.find("OK", 0) > 0):
            PrintLog('CreateDevice <-- OK')
            GetDeviceList()
            NewIdx = SearchDeviceIdx(NewID, NewUnit)
            break
        if (UrlLine.find("ERR", 0) > 0):
            PrintLog('CreateDevice <-- ERROR')
            StopPending = 1
            break
    # end process result from urlopen

    return NewIdx

# CreateDevice


def SetDeviceState(NewIdx, NewState):

    global StopPending

    PrintLog('SetDeviceState (Idx=' + str(NewIdx) + ') --> ' + NewState)

    UrlCmd = DomoticzIP + '/json.htm'
    UrlCmd = UrlCmd + '?type=command'
    UrlCmd = UrlCmd + '&param=switchlight'
    UrlCmd = UrlCmd + '&idx=' + NewIdx
    if (NewState == '0'):
        UrlCmd = UrlCmd + '&switchcmd=Off'
    else:
        UrlCmd = UrlCmd + '&switchcmd=On'

    UrlResult=urllib.request.urlopen(UrlCmd)
    for UrlLine in UrlResult.readlines():
        UrlLine = UrlLine.decode('utf-8')
        if (UrlLine.find("OK", 0) > 0):
            # PrintLog('SetDeviceState <-- OK')
            break
        if (UrlLine.find("ERR", 0) > 0):
            PrintLog(UrlCmd)
            PrintLog('SetDeviceState <-- ERROR')
            StopPending = 1
            break
    # end process result from urlopen

# SetDeviceState


def ResetArduino():

    StartTime = time.time()
    PrintLog('Reset Arduino...')
    PinForResetArduino = 18 # GPIO=1, BCM number=18, Physical=12
    GPIO.setwarnings(False)
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(PinForResetArduino, GPIO.OUT)
    GPIO.output(PinForResetArduino, GPIO.LOW)
    time.sleep(1)
    GPIO.output(PinForResetArduino, GPIO.HIGH)

    Line = 'dummy'
    while ((time.time() - StartTime <= 10)): # wait max10 seconds
        while (Line != 'setup done.'):
            Line = GetFromArduino(3)

# ResetArduino


def SendToArduino(LineToSend):

    PrintLog('SendToArduino --> ' + LineToSend)

    for i in range(0, len(LineToSend)):
        data = bytes(LineToSend[i].encode())
        SerialToArduino.write(data)
    SerialToArduino.write(b'\n')
    SerialToArduino.flushOutput()

# SendToArduino


def GetFromArduino(SecondsToWait):

    global StopPending

    Line = ''
    StartTime = time.time()

    while ((time.time() - StartTime <= SecondsToWait + 0.05)):

        time.sleep(0.05) # 50 milli-seconds

        BytesToRead = SerialToArduino.inWaiting()

        if (BytesToRead > 0):
            PrintLog('')
            Line = SerialToArduino.readline()
            Line = Line.decode('cp1252')
            Line = Line.strip()
            PrintLog('GetFromArduino <-- ' + Line)
            break

        if (SecondsToWait == 0):
            # don't wait
            break

    if ((SecondsToWait > 0) and (Line) == ''):
        PrintLog('GetFromArduino: TimeOut')
        StopPending = 1

    return Line

# GetFromArduino


def ProcessFromArduino(Line):

    # A KaKu Switch-command received from Arduino is
    # (for ArdToDom) defined like (example):
    # KaKu 2AB34EF-12:0
    # where 2AB34EF is the Hexadecimal ID of the Device,
    # 12 is the Unit-number of the Device and the
    # 0 (behind the colon) is the required State. 0=Off; 1=On.

    PrintLog('ProcessFromArduino <-- ' + Line)

    if (Line not in LastArduinoCmds):

        WriteVariable(ArdToDomStateName, 'Process From Arduino')

        ArduinoID = Line[Line.find(' ', 0)+1:Line.find('-', 0)]
        ArduinoUnit = Line[Line.find('-', 0)+1:Line.find(':', 0)]
        ArduinoState = Line[Line.find(':', 0)+1]

        # See if device-ID is already known
        FoundIdx = SearchDeviceIdx(ArduinoID, ArduinoUnit)

        # if not found, create the device
        if (FoundIdx == ''):
            FoundIdx = CreateDevice(ArduinoID, ArduinoUnit)

        # Switch device On or Off
        if (FoundIdx > ''):
            SetDeviceState(FoundIdx, ArduinoState)

    # if Command was not recent already performed

    else:
        # Many KaKu Switches transmit their commands multiple times
        # to increase the chance that they are received.
        # To avoid double processing, this program saves received
        # and transmitted commands in a buffer for some seconds.
        # If a command is received a second time (or more) in that
        # period, the second (and more) command is ignored.
        PrintLog('Ignored - Double Cmd from Arduino.')

# ProcessFromArduino


def ProcessToArduino(SwitchRequest):

    PrintLog('ProcessToArduino <-- ' + SwitchRequest)

    CmdToArduino = '' # no command yet

    # Translate the Domoticz Request to a switch command
    # for Arduino.
    # The Domoticz-Request looks like 'idx: NewState' (45: Off)

    SearchIdx = SwitchRequest[0:SwitchRequest.find(':', 0)]
    if (SwitchRequest.find('Off') > 0):
        NewState = 0
    else:
        NewState = 1

    # Search for the Device in list
    FoundID = '' # no ID found yet
    FoundUnit = '' # No Unit either

    for Device in DeviceList:
        if (Device[2] == SearchIdx):
            FoundID = Device[0]
            FoundUnit = Device[1]
            break

    if (FoundID == ''):
        PrintLog('ProcessToArduino: Idx ' + SearchIdx + ' not found!')
    else:
        # Command to send to Arduino
        CmdToArduino = 'KaKu ' + FoundID + '-' + FoundUnit + ':' + str(NewState)

        if (CmdToArduino not in LastArduinoCmds):
            WriteVariable(ArdToDomStateName, 'Process To Arduino')
            PrintLog(Device[3] + ' --> ' + CmdToArduino)
            SendToArduino(CmdToArduino)

        else:
            # When ArdToDom updates a Device in Domoticz
            # to synchronize the state when a Switch command has
            # been transmitted, Domoticz will see this as a new event
            # and therefore trigger the script 'SetSwitchRequest'.
            # Consequently a change-request for that switch will be
            # send to ArdToDom.
            # This is recognized as a loopback situation and therefore
            # ignored.
            PrintLog('Ignored - Loopback from Domoticz')

    return CmdToArduino

# ProcessToArduino

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

# Start of program

PrintLog('Start')
PrintLog('Monitor Arduino and Domoticz')

Initialize()

LastIAmAliveTime = time.time()

while True: # run continuously

    if (StopPending != 0):
        WriteVariable(ArdToDomStateName, 'Stop Pending')
        PrintLog('Stop Pending ...')
        break

    # See if Arduino has received a command
    ArduinoCmd = GetFromArduino(0)

    if (ArduinoCmd[0:4] == 'KaKu'):
        ProcessFromArduino(ArduinoCmd)
        LastArduinoCmds.append(ArduinoCmd)
        LastArduinoCmdTime = time.time()

    time.sleep(0.05) # 50 milli-seconds

    # The request is (for ArdToDom) defined like: '45: Off',
    # where 45 is the idx of the Device within Domoticz
    # and On or Off indicate the desired New State.

    # See if Domoticz has a command to send
    SwitchRequest = ReadVariable(SwitchRequestIdx)
    if (SwitchRequest > ''):
        # After receiving the Switch Request, clear the variable:
        WriteVariable(SwitchRequestName, '') # Clear SwitchRequest

    if (SwitchRequest == 'stop'):
        StopPending = 1

    if ((SwitchRequest > '') and (StopPending == 0)):
        ArduinoCmd = ProcessToArduino(SwitchRequest)
        LastArduinoCmds.append(ArduinoCmd)
        LastArduinoCmdTime = time.time()

    time.sleep(0.05) # 50 milli-seconds

    # After some time clear history to allow same commands again
    if ((time.time() - LastArduinoCmdTime > 3)
    and (LastArduinoCmdTime != 0)):
        WriteVariable(ArdToDomStateName, 'Clear Cmd History')
        PrintLog('Clear Cmd History...')
        LastArduinoCmds = []
        LastArduinoCmdTime = 0

    if (time.time() - LastIAmAliveTime > 60):
        WriteVariable(ArdToDomStateName, 'Standby')
        LastIAmAliveTime = time.time()

SerialToArduino.close()
WriteVariable(ArdToDomStateName, 'Stopped')
PrintLog('Stopped')

Copy ArdToDom to the Raspberry Pi (I use FTP from my desktop) to
/home/pi/python_programs/ArdToDom.py

Make the program executable:

- Open a command shell

chmod +x /home/pi/python_programs/ArdToDom.py


- Open the Python 3 programming environment on your Raspberry Pi and open program ArdToDom.

Press F5 to run ArdToDom.

ArdToDom will in Domoticz create
- under Hardware the Virtual Hardware Placeholder
- User Variable SwitchRequest
- User Variable ArdToDom State
- A LUA script /home/pi/domoticz/scripts/lua/script_device_SetSwitchRequest.lua

Program ArdToDom will be started by this LUA script when an event occurs in Domoticz.
If no event occurs... it will not start.

To be sure that periodically events occur, I suggest that you install under 'Hardware' the Hardware-Device 'Motherboard Sensors' and then under Settings --> Devices activate sensors 'Internal Temperature' and 'CPU_Usage'.

If you want to know how program ArdToDom works, please open it and read the comment.
- - - - - - - - - - -
2 x Domoticz on Raspberry Pi; 2 x RFXtrx433; Aeotec Z-Stick
KlikAanKlikUit ICS-2000 as Relay
Aeotec MultiSensors 6; Danfoss Z Thermostats 014G0013; Kaku Switches, Z-wave Switches

mjdb
Posts: 87
Joined: Thursday 12 January 2017 14:38
Target OS: Raspberry Pi
Domoticz version: all beta
Location: NL - Pijnacker
Contact:

Domoticz on 30 Euro hardware - Final Thoughts

Post by mjdb » Wednesday 02 August 2017 15:31

I started this project for my personal needs. I was surprised about the outcome. It works and - actually - quite good. The range of the receiver and transmitter are at least as good as my RfxTrx433E devices.

So, I thought to share it with you. However, this is not a project that I will develop further or that I can give support upon. Please take from it what you like. Build the whole project or let you inspire for your own developments.

If you have idea's and improvements, don't hesitate to post them here for others.

And... don't forget to enjoy.
- - - - - - - - - - -
2 x Domoticz on Raspberry Pi; 2 x RFXtrx433; Aeotec Z-Stick
KlikAanKlikUit ICS-2000 as Relay
Aeotec MultiSensors 6; Danfoss Z Thermostats 014G0013; Kaku Switches, Z-wave Switches

wergeld
Posts: 18
Joined: Friday 01 January 2016 0:29
Target OS: Raspberry Pi
Domoticz version:
Contact:

Re: Domoticz on 30 Euro hardware - All in (for KaKu/Coco)

Post by wergeld » Thursday 03 August 2017 19:28

This looks really cool. Excellent write up.

brommetje
Posts: 54
Joined: Sunday 16 February 2014 18:40
Target OS: Raspberry Pi
Domoticz version: v3.9139
Location: NL
Contact:

Re: Domoticz on 30 Euro hardware - All in (for KaKu/Coco)

Post by brommetje » Thursday 03 August 2017 23:09

Great post, Thanks.

PauldH
Posts: 5
Joined: Tuesday 24 October 2017 16:39
Target OS: Linux
Domoticz version: v3.8720
Contact:

Re: Domoticz on 30 Euro hardware - All in (for KaKu/Coco)

Post by PauldH » Saturday 25 November 2017 17:30

Hi @mjdb,

Really cool project and the last few weeks I'm trying to build a slave installation using your instructions. I'm really far and have assembled a print with all the components and a raspberry with a already connected Domoticz master/slave install.

There were a few point where I had to use a workaround but almost all instructions nailed it. Great work!

When I installed and started the Arduino IDE trough RDP the Arduino interface consists of only a title bar and the rest is a grey screen. I’ve worked around this using a USB upload option.

I only have some Kaku powerplugs to test So I’ve tested the Arduino using some test sketches and all seems well. The only thig I don’t understand is how to connect the powerplug to Domoticz now that everything is setup.
2 x Domoticz on Raspberry Pi, 1x on Ubuntu and created some test dockers; Aeotec Z-Stick
Kaku Switches and remote, Z-wave Switches

PauldH
Posts: 5
Joined: Tuesday 24 October 2017 16:39
Target OS: Linux
Domoticz version: v3.8720
Contact:

Re: Domoticz on 30 Euro hardware - All in (for KaKu/Coco)

Post by PauldH » Thursday 28 December 2017 16:23

I've bought a Kaku remote and the ardino serial monitor prompts the correct output when I press a button. But how can I configure Domoticz to send the command trough the transmitter to the outlet. There is a Dummy device in Hardware and the logs are good, it must be something that binds the IDX to the Kaku command send trough the transmitter, just can't figure it out.

The output of the log:

Code: Select all

 2017-12-28 14:21:41.129 (Dummy) Light/Switch (TestSwitch)
2017-12-28 14:21:41.139 LUA: (SetSwitchRequest) 7: On
2017-12-28 14:21:41.141 EventSystem: Script event triggered: /home/pi/domoticz/scripts/lua/script_device_SetSwitchRequest.lua
2017-12-28 14:21:44.775 User: Admin initiated a switch command (7/TestSwitch/Off)
2017-12-28 14:21:44.799 (Dummy) Light/Switch (TestSwitch)
2017-12-28 14:21:44.807 LUA: (SetSwitchRequest) 7: Off
2017-12-28 14:21:44.809 EventSystem: Script event triggered: /home/pi/domoticz/scripts/lua/script_device_SetSwitchRequest.lua
2 x Domoticz on Raspberry Pi, 1x on Ubuntu and created some test dockers; Aeotec Z-Stick
Kaku Switches and remote, Z-wave Switches

bmonninkhof
Posts: 1
Joined: Monday 03 September 2018 9:55
Target OS: OS X
Domoticz version:
Contact:

Re: Domoticz on 30 Euro hardware - All in (for KaKu/Coco)

Post by bmonninkhof » Monday 03 September 2018 10:01

First of all I want to make a compliment for making this project!

I am trying to install a sort of similar installation, but with one major difference, I connect my Arduino Nano with USB with my Raspberry Pi.
Could there be an instruction for that setup?

Post Reply

Who is online

Users browsing this forum: No registered users and 2 guests