Pax Calima bluetooth controlled fan

Python and python framework
Post Reply
Ric68
Posts: 22
Joined: Wednesday 28 January 2015 18:47
Target OS: Raspberry Pi
Domoticz version:
Contact:

Pax Calima bluetooth controlled fan

Post by Ric68 » Saturday 23 June 2018 17:59

Hi,

I have installed pycalima, which is a python interface to control Pax Calima fan.

As I am not very experienced in Python, I thought I could at least show how to read the values from Calima into Domoticz and if someone has the knowledge of how to write to the fan using virtual dimmer, I would be very happy. I have tried different methods, but it has failed, probably due to the fact(?) that domoticz is running another instance of python, so any libraries that I install is not seen, but it might be for other reasons.

The installation of pycalima was not completely straightforward for me, but I managed to get it to work after installing some extra libraries.

You have to add 4 virtual sensors, Temp/Humidity, Lux, Percentage and Waterflow sensor. The Waterflow (airflow) is scaled as l/s to more easily compare with the Pax app which shows the value in l/s. Further more, the Humidity sensor goes to 0 when the humidity is normalized, so I assume that it is an offset of the normal humidity level (but uncertain if it is additive or multiplicative offset). I have also scaled the percentage to flow as a simple linear function. Looking at the data, it seems to be somewhat quadratic, but the error is relatively small.

Here is the code that runs on cron every 5:th minute. Remove some of the hashtags to get debug information. I assume that there is smarter ways to parse the data, so feel free to give some hints

Code: Select all

from Calima import Calima
import json
import requests

CALIMA_MAC = "xx:xx:xx:xx:xx:xx"
CALIMA_ID =  "12345678"

fan = Calima(CALIMA_MAC, CALIMA_ID)
state = str(fan.getStateShort())
fan.disconnect()

# Find temperature
pos = state.find("Temp=") 
pos2 = state.find(",",int(pos))
Temp = state[pos+5:pos2]

# Find humidity
pos = state.find("Humidity=") 
pos2 = state.find(",",int(pos))
Hum = float(state[pos+9:pos2])/10
Hum = str(Hum)

# Find light
pos = state.find("Light=") 
pos2 = state.find(",",int(pos))
Lux = state[pos+6:pos2]

# Find speed
pos = state.find("RPM=") 
pos2 = state.find(",",int(pos))
RPM = state[pos+4:pos2]
Flow = round(0.0137 * float(RPM) - 3.59,1)

httpurl = 'http://127.0.0.1:8080/json.htm?type=command&param=udevice&idx=30&nvalue=0&svalue=' + Temp + ';' + Hum + ';0' 
r = requests.get(httpurl)

httpurl = 'http://127.0.0.1:8080/json.htm?type=command&param=udevice&idx=31&svalue=' + Lux 
r = requests.get(httpurl)

httpurl = 'http://127.0.0.1:8080/json.htm?type=command&param=udevice&idx=32&nvalue=0&svalue=' + str(Flow) 
r = requests.get(httpurl)

httpurl = 'http://127.0.0.1:8080/json.htm?type=command&param=udevice&idx=33&nvalue=0&svalue=' + str(round(int(RPM)/2500*100,1)) 
r = requests.get(httpurl)

Ric68
Posts: 22
Joined: Wednesday 28 January 2015 18:47
Target OS: Raspberry Pi
Domoticz version:
Contact:

Re: Pax Calima bluetooth controlled fan

Post by Ric68 » Tuesday 26 June 2018 12:56

So I have a script that can control the fan speed based on the virtual sensor level, when I am running it from the shell

Code: Select all

from pycalima.Calima import Calima
import bluepy
import json
import requests

CALIMA_MAC = "xx:xx:xx:xx:xx:xx"
CALIMA_ID =  "12345678"


# Get level of virtual sensor
httpurl = 'http://127.0.0.1:8080/json.htm?type=devices&rid=36' 
r = requests.get(httpurl)
status = str(r.json())
pos = status.find('LevelInt')
pos2 = status.find(',',pos)
pct = float(status[pos+10:pos2])
speed = int(round(pct*24/25,0)*25)

# write level to fan
fan = Calima(CALIMA_MAC, CALIMA_ID)
fan.setFanSpeedSettings(2150,1200,speed)
fan.disconnect()
But running it from the "On Action" or "Off Action"

Code: Select all

  
script:///usr/bin/python3  /home/pi/domoticz/scripts/python/script_device_BathroomFan_demo.py
generates error 256. I have -rwxrwxrwx as setting on the actual file.

Code: Select all

2018-06-26 12:27:54.877 Error: Error executing script command (/usr/bin/python3). returned: 256
I could naturally run it using cron, which would give me the function that I request, though with an additional delay of up to 1 minute, which of course will work, but is there some easy solution to get the script to run as I would want?

toast
Posts: 12
Joined: Wednesday 16 November 2016 13:51
Target OS: Linux
Domoticz version:
Contact:

Re: Pax Calima bluetooth controlled fan

Post by toast » Monday 02 July 2018 20:32

Hi,
I just installed mine and have it setup working.
I modified the run.py script that came with the code to include similar to your json command for updating the virtual sensors. E.g. I adjusted with my preferred speed settings and added json update commands to the end of the script. In run.py the fan is only updated with new settings if it detects that the power has been lost. My domoticz is dunning on a different host and i have a rpi that controls the fan. I run a cron job on my rpi that update the sensors in Domoticz every minute in the morning, every 5 minutes during the day and every 10 minutes during the night

It would be interesting to add a correct formula for humidity, lux and air flow values (if such exist). I have not tested that much yet but for sure I can tell that the light value is not lux and the humidity is not RH%
I then have added (just for testing) a switch in Domoticz executing a script:

Code: Select all

script://fan_5min.sh

Code: Select all

cat /opt/src/domoticz/scripts/fan_5min.sh 
#!/bin/sh
ssh pi@192.168.0.22 "python3 /opt/src/pycalima/fan_5min.py"

Code: Select all

cat /opt/src/pycalima/fan_5min.py
from pycalima.Calima import Calima

import json
import requests

CALIMA_MAC = "58:2b:db:01:b0:bb"
CALIMA_ID =  "xxxxxxxx"

fan = Calima(CALIMA_MAC, CALIMA_ID)
#fan.setFanSpeedSettings(2100,1000,1000)
#print(fan.getFanSpeedSettings())

#Start fan
fan.setBoostMode(1,975,300)

#fan.setSensorsSensitivity(3,1)
#print(fan.getSensorsSensitivity())
state = str(fan.getStateShort())
fan.disconnect()
print(state)

# Update Domoticz just because I like it...
# Find humidity
pos = state.find("Humidity=") 
pos2 = state.find(",",int(pos))
Hum = float(state[pos+9:pos2])/10
Hum = str(Hum)
#Hum = state[pos+9:pos2]

# Find temperature
pos = state.find("Temp=")
pos2 = state.find(",",int(pos))
Temp = state[pos+5:pos2]

# Find light
pos = state.find("Light=") 
pos2 = state.find(",",int(pos))
Lux = state[pos+6:pos2]

# Find speed
pos = state.find("RPM=") 
pos2 = state.find(",",int(pos))
RPM = state[pos+4:pos2]
Flow = round(0.0125 * float(RPM),1)

httpurl = 'http://192.168.0.3:8080/json.htm?type=command&param=udevice&idx=135&nvalue=0&svalue=' + Temp + ';' + Hum + ';0' 
r = requests.get(httpurl)

httpurl = 'http://192.168.0.3:8080/json.htm?type=command&param=udevice&idx=136&svalue=' + Lux 
r = requests.get(httpurl)

httpurl = 'http://192.168.0.3:8080/json.htm?type=command&param=udevice&idx=138&nvalue=0&svalue=' + str(Flow) 
r = requests.get(httpurl)

httpurl = 'http://192.168.0.3:8080/json.htm?type=command&param=udevice&idx=137&nvalue=0&svalue=' + str(round(int(RPM)/2400*100,1)) 
r = requests.get(httpurl)

#Reset switch state
httpurl = 'http://192.168.0.3:8080/json.htm?type=command&param=udevice&idx=139&svalue=0'
r = requests.get(httpurl)
Script that runs in cron:

Code: Select all

crontab -l
# m h  dom mon dow   command
#*/5 7,8 * * * python3 /opt/src/pycalima/fan.py
* 7,8 * * * python3 /opt/src/pycalima/fan.py
*/5 9,10,11,12,13,14,15,16,17,18,19,20,21 * * * python3 /opt/src/pycalima/fan.py
*/10 22,0,1,2,3,4,5,6 * * * python3 /opt/src/pycalima/fan.py

Code: Select all

cat fan.py
#!/usr/bin/env python3
import sys, time, datetime, json, requests
from pycalima.Calima import Calima

# Here you can specify your Calimas MAC address and pincode
# Address you can find by using cmdline.poy script and -l parameter
# Pincode is found on the backside of the manual or on one of the
# feet of the motor plug component
fan = Calima("58:2b:db:01:b0:bb", "12234235")
appliedSettings = 0

try:
#  print(fan.getAlias())
#  print(fan.getState())


  # If Clock is not set then there has been a power failure,
  # so just in case we will write the settings to Calima and set time
  if fan.getIsClockSet() == "02":
    # Lets preconfigure Calima by setting these values

    # Set appliedSettings to 1 since we are applying the settings
    # We then output its value at the bottom so we know if there was a power cycle
    appliedSettings = 1
  
    # Start off by setting time and then wait 2 seconds for it to settle before polling for time
    fan.setTimeToNow()
    time.sleep(2)
    #fan.getTime()
  
    # Setting Fan speed, I am not using light sensor therefore same value in fields 2 and 3
    fan.setFanSpeedSettings(2100,1100,1000)

    # Sets humidity sensivitity and light sensitivity. If set to 0 then sensor triggering will be off
    fan.setSensorsSensitivity(3,1)
    #fan.getSensorsSensitivity()
  
    # Set Light sensor delay and running times. Just setting these even if I do not use light sensor for now.
    fan.setLightSensorSettings(5,5)
    #fan.getLightSensorSettings()
  
    # Set automatic purge cycles which is max speed for X minutes every 12 hours, 0=disabled, 1=30min, 2=60min, 3=90min
    # Disable it for now as I want to control it from other Smart Home systems
    fan.setAutomaticCycles(0)
    #fan.getAutomaticCycles()
  
    # Setting Silent hours
    fan.setSilentHours(1,22,0,6,0)
    #fan.getSilentHours()
  
    # Set trickle days
    fan.setTrickleDays(1,1)
    #fan.getTrickleDays()


  # Need to iterate through namedtuple and print its contents
  # concatenating appliedSettings value and prefixing with timestamp without newline
  currentState = fan.getStateShort()
  timeStamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S ')
  print(timeStamp,end='')

  for item in currentState._fields:
    print("{}={} ".format(item,getattr(currentState, item)),end='')
  print("appliedSettings={}".format(appliedSettings))

except Exception as e:
  print(e)
  fan.disconnect()

state = str(currentState)

# Find humidity
pos = state.find("Humidity=") 
pos2 = state.find(",",int(pos))
Hum = float(state[pos+9:pos2])/10
Hum = str(Hum)
#Hum = state[pos+9:pos2]

# Find temperature
pos = state.find("Temp=")
pos2 = state.find(",",int(pos))
Temp = state[pos+5:pos2]

# Find light
pos = state.find("Light=") 
pos2 = state.find(",",int(pos))
Lux = state[pos+6:pos2]

# Find speed
pos = state.find("RPM=") 
pos2 = state.find(",",int(pos))
RPM = state[pos+4:pos2]
Flow = round(0.0125 * float(RPM),1)

httpurl = 'http://192.168.0.3:8080/json.htm?type=command&param=udevice&idx=135&nvalue=0&svalue=' + Temp + ';' + Hum + ';0' 
r = requests.get(httpurl)

httpurl = 'http://192.168.0.3:8080/json.htm?type=command&param=udevice&idx=136&svalue=' + Lux 
r = requests.get(httpurl)

httpurl = 'http://192.168.0.3:8080/json.htm?type=command&param=udevice&idx=138&nvalue=0&svalue=' + str(Flow) 
r = requests.get(httpurl)

httpurl = 'http://192.168.0.3:8080/json.htm?type=command&param=udevice&idx=137&nvalue=0&svalue=' + str(round(int(RPM)/2400*100,1)) 
r = requests.get(httpurl)

toast
Posts: 12
Joined: Wednesday 16 November 2016 13:51
Target OS: Linux
Domoticz version:
Contact:

Re: Pax Calima bluetooth controlled fan

Post by toast » Monday 02 July 2018 21:51

My attempt to use the fan lux sensor to switch on lights if it gets darker before sunset..

Code: Select all

-- Check the wiki at
-- http://www.domoticz.com/wiki/%27dzVents%27:_next_generation_LUA_scripting
return {
	active = true, -- set to false to disable this script

	on = {
	    timer = { 'every 10 minutes between 120 minutes before sunset and sunset' } -- Light always turn on 20 minutes before sunset by group timer
	},

    execute = function(domoticz)
        if (domoticz.devices('$[E] Dark').state == 'Off' and domoticz.devices('[E] Toilet Lux').lux < 8) then -- Check if not already triggered
            domoticz.groups('[E] Lights').switchOn()
        end
    end
}

toast
Posts: 12
Joined: Wednesday 16 November 2016 13:51
Target OS: Linux
Domoticz version:
Contact:

Re: Pax Calima bluetooth controlled fan

Post by toast » Thursday 05 July 2018 22:04

Maybe logarithmic scale for the humidity...

Code: Select all

Hum = math.log2(float(state[pos+9:pos2]))*10

Post Reply

Who is online

Users browsing this forum: No registered users and 3 guests