Light control with NodeMCU v3

Révision en cours
Light control with NodeMCU v3

The goal is to control all my lights with Domoticz and my wall switches.

A NodeMCU v3 will be used as the controller, a PCF8574 board to extand I/O, a 4 relays board to switch lights on/off and a mini AC-DC converter.

ESPeasy is the firmware of the main board and Domoticz the home automation manager.

### Home configuration
Lights in the home are simple or 3-way switches.
By the way, all cables from live, neutral and between switches and lights are gathered in a connecting box as the following:
![wiring_present](https://user-images.githubusercontent.com/3757683/40920482-ff1a231e-680c-11e8-99fb-99fe8a2e9e80.png)

The house is divided into two part, one for living rooms with 6 lights (2 are peers) and one for sleeping rooms with 7 independant lights. Thus there are two boxes.

The idea is to implement all the stuff in these boxes.
The configuration will be the following one :
![wiring_futur](https://user-images.githubusercontent.com/3757683/40920580-45f190ba-680d-11e8-87e6-cf5879678d71.png)

I will only explain living rooms configuration.

### Hardware
As NodeMCU as few GPIO pin available, PCF8574A boards will be used to expand I/O ports.
For living rooms, one board is mandatory whereas in sleeping rooms two boards are mandatory.

GPIOs 4 (D1) and 5 (D2) will be used as SDA and SCL.
To be sure, SDA and SCL lines are pulled-up with a 4,7k resistor (not mandatory for one board on the line).

Ports of the PCF8574 are _open drain_. During boot, ESPeasy initialize output as low and input as high (must be pulled-up).
By the way, logic on the relays board is inverted, 0 to active a relay and 1 to deactivate.

To control relays, output pins are directly connected to input pins of the relay board, and PCF8574 pins are activated (send 1).

To monitor switches, input pins are connected to ground through switches and to a pull-up resistor (33k).

### Software
Lights have to :
 - be controlled by Domoticz,
- -  be autonomouscontrolled (manual switch in case of network lost) and report back the state of lights toby Domoticz.,
-   be autonomous (manual switch in case of network lost) and report back the state of lights to Domoticz.

#### ESPeasy
##### Devices
There are 4 devices for input on PCF8574 and one dummy device for variables.

-   _BoutonEnt_ monitoring port 69 of PCF8574, variable named _etat_,
-   _BoutonSal_ monitoring port 70 of PCF8574, variable named _etat_,
-   _BoutonSmc_ monitoring port 71 of PCF8574, variable named _etat_,
-   _BoutonCel_ monitoring port 72 of PCF8574, variable named _etat_,
-   _EtatRelais_ keeping states of relays, variables named _entree_, _salon_, _salleamangercuisine_, _cellier_.
 
To control relays, there is no need to add devices, the control is done with the command ``pcfgpio, port, value``.
 
##### Rules
######After Wirelesssome switchtries and fails, rules are divided into 2 files.
###### Manual switch
Rules1.txt to control boot commands :
~~~
on System#boot do
pcfgpio,65,1
pcfgpio,66,1
pcfgpio,67,1
pcfgpio,68,1
taskvalueset 5,1,0
taskvalueset 5,2,0
taskvalueset 5,3,0
taskvalueset 5,4,0
Delay, 1000
Publish domoticz/in, {"command": "setuservariable", "idx": 1, "value": [EtatRelais#entree]}
Publish domoticz/in, {"command": "setuservariable", "idx": 2, "value": [EtatRelais#salon]}
Publish domoticz/in, {"command": "setuservariable", "idx": 3, "value": [EtatRelais#salleamangercuisine]}
Publish domoticz/in, {"command": "setuservariable", "idx": 4, "value": [EtatRelais#cellier]}
endon
~~~

Rules2.txt to manage buttons and commands :
~~~
on BoutonEnt#etat do
event,Toggleentree
endon

on BoutonSal#etat do
event,Togglesalon
endon

on BoutonSmc#etat do
event,Togglesalleamangercuisine
endon

on BoutonCel#etat do
event,Togglecellier
endon

on Toggleentree do
if [EtatRelais#entree]=1
pcfgpio,65,1
taskvalueset 5,1,0
else
pcfgpio,65,0
taskvalueset 5,1,1
endif
delay,1000
Publish domoticz/in, {"command": "setuservariable", "idx": 1, "value": [EtatRelais#entree]}
endon

on Togglesalon do
if [EtatRelais#salon]=1
pcfgpio,66,1
taskvalueset 5,2,0
else
pcfgpio,66,0
taskvalueset 5,2,1
endif
delay,1000
Publish domoticz/in, {"command": "setuservariable", "idx": 2, "value": [EtatRelais#salon]}
endon

on Togglesalleamangercuisine do
if [EtatRelais#salleamangercuisine]=1
pcfgpio,67,1
taskvalueset 5,3,0
else
pcfgpio,67, 0
taskvalueset 5,3,1
endif
delay,1000
Publish domoticz/in, {"command": "setuservariable", "idx": 3, "value": [EtatRelais#salleamangercuisine]}
endon

on Togglecellier do
if [EtatRelais#cellier]=1
pcfgpio,68,1
taskvalueset 5,4,0
else
pcfgpio,68,0
taskvalueset 5,4,1
endif
delay,1000
Publish domoticz/in, {"command": "setuservariable", "idx": 4, "value": [EtatRelais#cellier]}
endon
~~~

By the way, there are some trouble as the board reboot sometimes when a button is pushed.  
Is it a problem with the power supply which is under rated or just to cheap ?
Or something else...  
First step, add a timer to reboot the board every 6h.  
Second step, try an other power supply.

#### Domoticz
##### DeviceDevices
4 virtual switches are created :  

-   Lumière entrée
-   Lumière salon
-   Lumière salle à manger / cuisine
-   Lumière cellier

##### UserVariables
To handle states of lights, a user variable is used :  
-   EtatLumiereentree as integer
-   EtatLumieresalon as integer
-   EtatLumieresalleamangercuisine as integer
-   EtatLumierecellier as integer

##### Events
TheEvents ruleare iscoded sharedin withdzVents the [RollerShutter control](indexlanguage.php?file=RollerShutter.md).

~~~
commandArray = {}

local relaisPav = { ["entrée"] = 65, ["salon"] = 66, ["salle à manger / cuisine"] = 67 }
local relaisCha = { ["WC"] = 66, ["salle de bain"] = 68, ["couloir"] = 70, ["Jaouen"] = 72, ["Leonie"] = 74, ["parents"] = 76 }

--They Normalizeare adivided giveninto string2 files.
function stripChars(str)
	local tableAccents = {}
		tableAccents["à"] = "a"
		tableAccents["ç"] = "c"
		tableAccents["è"] = "e"
		tableAccents["é"] = "e"
		tableAccents["ê"] = "e"
		tableAccents["ù"] = "u"
	local normalizedString = ''

	forlights_cmd strCharto insend string.gmatch(str,command "([%z\1-\127\194-\244][\128-\191]*)")to doESPeasy from virtual switches :
		if tableAccents[strChar] ~= nil then~~
			normalizedStringreturn = normalizedString..tableAccents[strChar]{
		elseon = {
			normalizedStringdevices = normalizedString..strChar{
			'Lumière*'
		}
	},
	execute = function(domoticz, device)
		local relaisPav = { entree = true, salon = true, salleamangercuisine = true, cellier = true }
        local relaisCha = { wc = true, salledebain = true, couloir = true, jaouen = true, leonie = true, parents = true }
        local piece = string.lower(domoticz.helpers.stripChars(device.name:gsub('Lumière ','')))
        if relaisPav[piece] then
            domoticz.openURL('http://Relais_PaV/control?cmd=event,Toggle'..piece)
        elseif relaisCha[piece] then
            domoticz.openURL('http://Relais_Cha/control?cmd=event,Toggle'..piece)
        end
	end
}
~~~

lights_state to change state of virtual switches after a local event :
~~~
return {
	on = {
		variables = {
		    'EtatLumiere*'
		}
	},
	execute = function(domoticz, variable)
	    -- UserVariable - Switch
	    local groupVarSwi = {entree = 4, salon = 5, salleamangercuisine = 6, cellier = 7, wc = 11,
	        couloir = 12, salledebain = 13, leonie = 14, jaouen = 15, parents = 16 }
	    --print('variable.value : '..variable.value)
	    --print('variable.id : '..variable.id)
	    local piece = string.lower(domoticz.helpers.stripChars(variable.name:gsub('EtatLumiere','')))
		if variable.value == 1 and domoticz.devices(groupVarSwi[piece]).state == "Off" then
		    domoticz.devices(groupVarSwi[piece]).switchOn().silent()
	    elseif variable.value == 0 and domoticz.devices(groupVarSwi[piece]).state == "On" then
	        domoticz.devices(groupVarSwi[piece]).switchOff().silent()
		end
	end
 return normalizedString}
end~~~

for i, v in pairs(devicechanged) do
	--print("i : "..i)
	--print("v : "..tostring(v))
	if (i:sub(1,5) == 'Volet') then
		i=tostring(i)
		addr=i:gsub('Volet ','VR_')
		espURL = "http://"..addr.."/control?cmd="
		if otherdevices[i] == 'Open' then
			pourcentage = 0
		elseif otherdevices[i] == 'Closed' then
			pourcentage = 100
		else
			pourcentage = tonumber(otherdevices_svalues[i])
		end
		-- print("Valeur d'ouverture du volet :"..pourcentage)
		commandArray[1] = {['OpenURL'] = espURL..'event,move='..pourcentage}
	end

	ifIn (stripChars(i)order to handle special character, a generic dzVents helpers function is created :sub(1,7) == "Lumiere") then
			--print("i : "..stripChars(i))~~~
			--print("vreturn : "..tostring(v)){
			if relaisPav[i:gsub('Lumière ','')] thenhelpers = {
				commandArray[1] = {['OpenURL'] = 'http://Relais_PaV/control?cmd=event,Toggle='..relaisPav[i:gsub('Lumière ','')]}   -- Normalize a given string
			elseif relaisCha[i:gsub       stripChars = function('Lumière ',''str)] then
				commandArray[1]            local tableAccents = {['OpenURL'] = 'http://Relais_Cha/control?cmd=event,Toggle='..relaisCha[i:gsub('Lumière ','')]}
			end            tableAccents["à"] = "a"
	end            tableAccents["ç"] = "c"
end            tableAccents["è"] = "e"
            tableAccents["é"] = "e"
return commandArray           tableAccents["ê"] = "e"
            tableAccents["ù"] = "u"
            tableAccents["\\"] = ""
            tableAccents["/"] = ""
            tableAccents[" "] = ""
            local normalizedString = ''
        
            for strChar in string.gmatch(str, "([%z\1-\127\194-\244][\128-\191]*)") do
                if tableAccents[strChar] ~= nil then
                    normalizedString = normalizedString..tableAccents[strChar]
                else
                    normalizedString = normalizedString..strChar
                end
            end
            return normalizedString
        end,
   }
}
~~~
##### Switches
Révision 1
Light control with NodeMCU v3

The goal is to control all my lights with Domoticz and my wall switches.

A NodeMCU v3 will be used as the controller, a PCF8574 board to extand I/O, a 4 relays board to switch lights on/off and a mini AC-DC converter.

ESPeasy is the firmware of the main board and Domoticz the home automation manager.

### Home configuration
Lights in the home are simple or 3-way switches.
By the way, all cables from live, neutral and between switches and lights are gathered in a connecting box as the following:
![wiring_present](https://user-images.githubusercontent.com/3757683/40920482-ff1a231e-680c-11e8-99fb-99fe8a2e9e80.png)

The house is divided into two part, one for living rooms with 6 lights (2 are peers) and one for sleeping rooms with 7 independant lights. Thus there are two boxes.

The idea is to implement all the stuff in these boxes.
The configuration will be the following one :
![wiring_futur](https://user-images.githubusercontent.com/3757683/40920580-45f190ba-680d-11e8-87e6-cf5879678d71.png)

### Hardware
As NodeMCU as few GPIO pin available, PCF8574A boards will be used to expand I/O ports.
For living rooms, one board is mandatory whereas in sleeping rooms two boards are mandatory.

GPIOs 4 (D1) and 5 (D2) will be used as SDA and SCL.

### Software
Lights have to :
 - be controlled by Domoticz,
 - be autonomous (manual switch in case of network lost) and report back the state of lights to Domoticz.

#### ESPeasy
##### Devices
##### Rules
###### Wireless switch
###### Manual switch
#### Domoticz
##### Device
##### Events
The rule is shared with the [RollerShutter control](index.php?file=RollerShutter.md).

~~~
commandArray = {}

local relaisPav = { ["entrée"] = 65, ["salon"] = 66, ["salle à manger / cuisine"] = 67 }
local relaisCha = { ["WC"] = 66, ["salle de bain"] = 68, ["couloir"] = 70, ["Jaouen"] = 72, ["Leonie"] = 74, ["parents"] = 76 }

-- Normalize a given string
function stripChars(str)
	local tableAccents = {}
		tableAccents["à"] = "a"
		tableAccents["ç"] = "c"
		tableAccents["è"] = "e"
		tableAccents["é"] = "e"
		tableAccents["ê"] = "e"
		tableAccents["ù"] = "u"
	local normalizedString = ''

	for strChar in string.gmatch(str, "([%z\1-\127\194-\244][\128-\191]*)") do
		if tableAccents[strChar] ~= nil then
			normalizedString = normalizedString..tableAccents[strChar]
		else
			normalizedString = normalizedString..strChar
		end
	end
 return normalizedString
end

for i, v in pairs(devicechanged) do
	--print("i : "..i)
	--print("v : "..tostring(v))
	if (i:sub(1,5) == 'Volet') then
		i=tostring(i)
		addr=i:gsub('Volet ','VR_')
		espURL = "http://"..addr.."/control?cmd="
		if otherdevices[i] == 'Open' then
			pourcentage = 0
		elseif otherdevices[i] == 'Closed' then
			pourcentage = 100
		else
			pourcentage = tonumber(otherdevices_svalues[i])
		end
		-- print("Valeur d'ouverture du volet :"..pourcentage)
		commandArray[1] = {['OpenURL'] = espURL..'event,move='..pourcentage}
	end

	if (stripChars(i):sub(1,7) == "Lumiere") then
			--print("i : "..stripChars(i))
			--print("v : "..tostring(v))
			if relaisPav[i:gsub('Lumière ','')] then
				commandArray[1] = {['OpenURL'] = 'http://Relais_PaV/control?cmd=event,Toggle='..relaisPav[i:gsub('Lumière ','')]}
			elseif relaisCha[i:gsub('Lumière ','')] then
				commandArray[1] = {['OpenURL'] = 'http://Relais_Cha/control?cmd=event,Toggle='..relaisCha[i:gsub('Lumière ','')]}
			end
	end
end

return commandArray
~~~
##### Switches
Retour