I created a Python script which creates a JSON output containing all devices/attributes listed in:
sudo -u openhab /usr/local/bin/icloud --username icloud_username --llist
The output can be easily parsed with json transformation (I use jython rules).
I guess the only downside will be the required reauthentication every 2 months as mentioned in the pyicloud documentation. But I’ll see …
Script (I put it in /etc/openhab/scripts/)
from pyicloud import PyiCloudService
import subprocess
# functions
def is_int(str):
try:
int(str)
return True
except ValueError:
return False
def is_float(str):
try:
float(str)
return True
except ValueError:
return False
# icloud username
username = "put_your_icloud_username_here"
inputString = subprocess.check_output(r'/usr/local/bin/icloud --username {0} --llist'.format(username.rstrip()), shell=True).decode(sys.stdout.encoding).strip().split("\n")
# initialize output
outputJson = ''
# read content line by line
for lineContent in inputString :
if '-----' in lineContent :
# remove last comma if a new device section begins
outputJson = outputJson.strip(',')
# add end bracket for previous device section
outputJson = outputJson + '},'
elif not ' - ' in lineContent :
# new device section does not contain " - "
outputJson = outputJson + '"' + lineContent.rstrip('\r\n') + '": {'
elif ' - ' in lineContent :
# add attribute name
outputJson = outputJson + '"' + lineContent.split()[0] + '": '
# add value without quotes when starting with {
if lineContent.rsplit(" - ",1)[1].rstrip('\r\n').startswith('{') :
outputJson = outputJson + lineContent.rsplit(" - ",1)[1].rstrip('\r\n') + ','
# add value without quotes when starting with [
elif lineContent.rsplit(" - ",1)[1].rstrip('\r\n').startswith('[') :
outputJson = outputJson + lineContent.rsplit(" - ",1)[1].rstrip('\r\n') + ','
# add value without quotes when value is integer
elif is_int(lineContent.rsplit(" - ",1)[1].rstrip('\r\n')) :
outputJson = outputJson + lineContent.rsplit(" - ",1)[1].rstrip('\r\n') + ','
# add value without quotes when value is float
elif is_float(lineContent.rsplit(" - ",1)[1].rstrip('\r\n')) :
outputJson = outputJson + lineContent.rsplit(" - ",1)[1].rstrip('\r\n') + ','
else :
# generally remove " in attribute value to prevent JSON parsing issues
outputJson = outputJson + '"' + lineContent.rsplit(" - ",1)[1].rstrip('\r\n').replace('"', '' ) + '",'
# clean-up outputJson
outputJson = '{' + outputJson.strip('},') + '} }'
outputJson = outputJson.replace("'", "\"")
outputJson = outputJson.replace("\"True\"", "true")
outputJson = outputJson.replace("True", "true")
outputJson = outputJson.replace("\"False\"", "false")
outputJson = outputJson.replace("False", "false")
outputJson = outputJson.replace(" None, ", " \"None\", ")
# print out result
print(outputJson)
Jython rule (I run it every 5 minutes):
"""
pyicloud control
Authorize once after reboot:
sudo -u openhab /usr/local/bin/icloud --username your_icloud_username
"""
from core.rules import rule
from core.triggers import when
from core.log import logging, LOG_PREFIX
from core import osgi
import sys
from core.actions import ScriptExecution
from core.actions import Transformation
import json
#pyicloud
from core.actions import Exec
from java.time import Duration
import math
log = logging.getLogger("org.eclipse.smarthome.model.script.pyicloud")
@rule("Pyicloud updater", description="updates the iCloud devices", tags=["pyicloud"])
@when("Time cron 5 1/5 * * * ?")
def pyicloudUpdater(event):
#log.warn("Rule: Pyicloud updater started.")
output = Exec.executeCommandLine(Duration.ofSeconds(15), "python3", "/etc/openhab/scripts/icloud.py")
##################
# iPhone
##################
#log.warn("iPhone")
batteryLevel = Transformation.transform("JSONPATH","$.iPhone.batteryLevel",output)
#log.warn("batteryLevel=" + batteryLevel)
deviceStatus = Transformation.transform("JSONPATH","$.iPhone.deviceStatus",output)
#log.warn("deviceStatus=" + deviceStatus)
timeStamp = Transformation.transform("JSONPATH","$.iPhone.location.timeStamp",output)
#log.warn("timeStamp=" + timeStamp)
latitude = Transformation.transform("JSONPATH","$.iPhone.location.latitude",output)
#log.warn("latitude=" + latitude)
longitude = Transformation.transform("JSONPATH","$.iPhone.location.longitude",output)
#log.warn("longitude=" + longitude)
latitudeAcc = Transformation.transform("JSONPATH","$.iPhone.location.horizontalAccuracy",output)
#log.warn("latitudeAcc=" + latitudeAcc)
longitudeAcc = Transformation.transform("JSONPATH","$.iPhone.location.verticalAccuracy",output)
#log.warn("longitudeAcc=" + longitudeAcc)
batteryStatus = Transformation.transform("JSONPATH","$.iPhone.batteryStatus",output)
#log.warn("batteryStatus=" + batteryStatus)
# update OH items
if deviceStatus.startswith("20") :
#log.warn("OK")
try:
accuracy = float(math.sqrt((float(latitudeAcc)*float(latitudeAcc)) + (float(longitudeAcc) * float(longitudeAcc))))
except:
accuracy = 0.0
events.postUpdate("iPhone_BatteryLevel",str(int(float(batteryLevel)*100)))
events.postUpdate("iPhone_Location",str(latitude + ", " + longitude + ", 0.0"))
events.postUpdate("iPhone_LocationLast",str(timeStamp))
events.postUpdate("iPhone_LocationAccuracy",str(accuracy))
events.postUpdate("iPhone_BatteryStatus",str(batteryStatus))
else :
events.postUpdate("iPhone_BatteryStatus","Unknown")
... add additional devices the same way here ...