#!/bin/bash
# Based on the openzwave python examples
# https://github.com/OpenZWave/python-openzwave/blob/master/examples/hello_world.py
import logging
import sys, os, time, socket

logging.basicConfig(level=logging.INFO)

logger = logging.getLogger('openzwave')

import openzwave
from openzwave.node import ZWaveNode
from openzwave.value import ZWaveValue
#from openzwave.scene import ZWaveScene
from openzwave.controller import ZWaveController
from openzwave.network import ZWaveNetwork
from openzwave.option import ZWaveOption

def init_spirit(node):
  assert node.product_name == "EUR_SPIRITZ Wall Radiator Thermostat"

  for val in node.values:
    print("val: {} label: {} data: {}".format(val, node.values[val].label, node.values[val].data_as_string))
    if node.values[val].label == "Temperature":
      node.dg_valid_temp = val
    if node.values[val].label == "Level":
      node.dg_valid_level = val
    if node.values[val].label == "Battery Level":
      node.dg_valid_batlevel = val
    if node.values[val].label == "Mode":
      node.dg_valid_mode = val
    if node.values[val].label == "Heat":
      node.dg_valid_heat = val
  for dim_valid in node.get_dimmers():
    print("Dimmer")
  node.dg_valid_dimmer = dim_valid

  cd_val = node.values[node.dg_valid_mode].check_data("Heat Eco")
  print("check data said: {}".format(cd_val))
  node.dg_overheat_count = 0

def graph_spirit(node,prefix,graphconn):
  node.request_state()
  time.sleep(5.0)
  temp=node.values[node.dg_valid_temp].data_as_string
  level=node.values[node.dg_valid_level].data_as_string
  bat=node.values[node.dg_valid_batlevel].data_as_string
  last_reqrtt = node.stats['lastRequestRTT']
  last_resprtt = node.stats['lastResponseRTT']

  now=time.time()
  local_now = time.localtime(now)
  print("@{}: temp: {} level: {} bat: {}".format(now, temp, level, bat))

  graphconn.send("{}.temp {} {}\n{}.level {} {}\n{}.bat {} {}\n{}.lastreqrtt {} {}\n{}.lastresprtt {} {}\n\n".
                 format(prefix,temp, now, prefix, level, now, prefix, bat, now,
                        prefix, last_reqrtt, now, prefix, last_resprtt, now).encode())
  print("stats:{}".format(node.stats))

def overheat_spirit(node, name):
  offset = -0.8
  temp=node.values[node.dg_valid_temp].data_as_string
  level=node.values[node.dg_valid_level].data_as_string
  heat=node.values[node.dg_valid_heat].data_as_string
  mode=node.values[node.dg_valid_mode].data_as_string

  print("check overheat for: {}  mode: {} heat: {} level: {} temp: {} count: {}".format(name, mode, heat, level, temp, node.dg_overheat_count))
  if mode == "Off":
    # It's off, so consider if we turn it back on, which we do
    # as long as the temperature is below the main set point
    # and the valve level is low so the firmware does sane things
    node.dg_overheat_count = 0
    if ((temp+0.0) < (heat+0.0)) and (level < 10):
      print("{}: Turning back on, temp: {} heat: {} level: {}", name, temp, heat, level)
      # I've seen the turn-back on, flip back to off by itself, so lets walk it
      # through off->heat
      node.values[node.dg_valid_mode].data = "Off"
      node.request_state()
      time.sleep(5.0)
      # TODO: Think whether it actually wants to go to heat-eco
      node.values[node.dg_valid_mode].data = "Heat"
  else:
    # It's on, see if it's over temperature and the level is high
    if ((temp+offset) > (heat+0.0)) and (level > 10):
      node.dg_overheat_count +=1
      print("{}: overheat!, temp: {} heat: {} level: {} count: {}".format(name, temp, heat, level, node.dg_overheat_count))

      if (node.dg_overheat_count >= 2):
        print("{}: Turning off overheat!, temp: {} heat: {} level: {} count: {}".format(name, temp, heat, level, node.dg_overheat_count))
        # Note: 'Off' doesn't actually seem to switch it off immediately, using the "Manufacturer Specific"
        # mode we can set a level - actually I can set 0 only
        # Manspec doesn't seem to read back right, so go through off first and then I think it reads back as
        # off
        node.values[node.dg_valid_mode].data = "Off"
        time.sleep(5.0)
        node.values[node.dg_valid_mode].data = "Manufacturer Specific"
        node.set_dimmer(node.dg_valid_dimmer, 0)
    else:
      node.dg_overheat_count = 0
      


device="/dev/serial/by-id/usb-0658_0200-if00"
TRV_davids=4
TRV_wbath=5
TRV_davidoffice=7

options = ZWaveOption(device,
                      user_path=".", cmd_line="")
options.set_log_file("OZW_Log.log")
options.set_append_log_file(False)
options.set_console_output(True)
options.set_save_log_level("Warning")
options.set_logging(True)
# There's a compile time setting thats set as 7 for max
options.set_driver_max_attempts(7)
options.lock()

network = ZWaveNetwork(options, autostart=False)

network.start()

#We wait for the network.
print("***** Waiting for network to become ready : ")
for i in range(0,90):
    if network.state>=network.STATE_READY:
        print("***** Network is ready")
        break
    else:
        sys.stdout.write(".")
        sys.stdout.flush()
        time.sleep(1.0)

time.sleep(5.0)

TRV_davids_node = network.nodes[TRV_davids]
init_spirit(TRV_davids_node)
TRV_wbath_node = network.nodes[TRV_wbath]
init_spirit(TRV_wbath_node)
TRV_davidoffice_node = network.nodes[TRV_davidoffice]
init_spirit(TRV_davidoffice_node)


sys.stdout.flush()

# Reset all devices to known safe state
TRV_davids_node.values[TRV_davids_node.dg_valid_mode].data = "Heat"
TRV_wbath_node.values[TRV_wbath_node.dg_valid_mode].data = "Heat"
TRV_davidoffice_node.values[TRV_davidoffice_node.dg_valid_mode].data = "Heat"

current_day = time.localtime(time.time()).tm_mday
today_done = {}

while True:
  carbonsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  carbonsock.connect(("localhost", 2003))

  graph_spirit(TRV_davids_node, "local.local.heating.trvdavid", carbonsock)
  graph_spirit(TRV_wbath_node, "local.local.heating.trvwbath", carbonsock)
  graph_spirit(TRV_davidoffice_node, "local.local.heating.trvdavidoffice", carbonsock)

  # actions
  now=time.time()
  local_now = time.localtime(now)
  # Midnight (or restart) - clear the list of done actions for the day
  now_day = local_now.tm_mday
  now_hour = local_now.tm_hour

  if now_day != current_day:
    print("New day {} -> {}".format(current_day,now_day))
    current_day = now_day
    today_done = {}

  # Bedroom doesn't need to be heated much in afternoon
  if (not "david_trv_afternoon" in today_done) and now_hour == 12:
    today_done["david_trv_afternoon"] = 1
    TRV_davids_node.values[TRV_davids_node.dg_valid_mode].data = "Heat Eco"
    print("Davids bedroom afternoon cool")

  # Bedroom does need to be warm in the evening
  if (not "david_trv_evening" in today_done) and now_hour == 18:
    today_done["david_trv_evening"] = 1
    TRV_davids_node.values[TRV_davids_node.dg_valid_mode].data = "Heat"
    print("Davids bedroom evening warm")

  # Check for overheating
  overheat_spirit(TRV_davidoffice_node, "davidoffice")
  overheat_spirit(TRV_davids_node, "davids")

  sys.stdout.flush()
  carbonsock.close()

  time.sleep(10*60)
