/****************************************************************************/
/*
 * MODULE              JN-AN-1162 JenNet-IP Smart Home
 *
 * DESCRIPTION         OccIllBulbConfig MIB Implementation
 */
/****************************************************************************/
/*
 * This software is owned by NXP B.V. and/or its supplier and is protected
 * under applicable copyright laws. All rights are reserved. We grant You,
 * and any third parties, a license to use this software solely and
 * exclusively on NXP products [NXP Microcontrollers such as JN5168, JN5164].
 * You, and any third parties must reproduce the copyright and warranty notice
 * and any other legend of ownership on each copy or partial copy of the
 * software.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * Copyright NXP B.V. 2014. All rights reserved
 */
/****************************************************************************/

/****************************************************************************/
/***        Include files                                                 ***/
/****************************************************************************/
/* Standard includes */
#include <string.h>
/* SDK includes */
#include <jendefs.h>
/* Hardware includes */
#include <AppHardwareApi.h>
#include <PeripheralRegs.h>
/* Stack includes */
#include <Api.h>
#include <AppApi.h>
#include <JIP.h>
#include <6LP.h>
#include <AccessFunctions.h>
/* JenOS includes */
#include <dbg.h>
#include <dbg_uart.h>
#include <os.h>
#include <pdm.h>
/* Application device includes */
#include "DeviceDefs.h"
#include "Node.h"
#include "Address.h"
#include "MibSensor.h"
#include "MibOccIllBulbConfig.h"
#include "MibOccupancyStatus.h"
#include "MibOccupancyMonitor.h"
#include "MibOccupancyControl.h"
#include "MibIlluminanceStatus.h"
#include "MibIlluminanceControl.h"
#include "MibBulb.h"
//#include "Uart.h"

/****************************************************************************/
/***        Macro Definitions                                             ***/
/****************************************************************************/
/* Debug */
#define DEBUG_OCC_ILL_BULB_CONFIG FALSE

/* Data access */
#define PS_MIB_OCCUPANCY_STATUS    ((tsMibOccupancyStatus *)    psMibOccIllBulbConfig->pvMibOccupancyStatus)
#define PS_MIB_OCCUPANCY_MONITOR   ((tsMibOccupancyMonitor *)   psMibOccIllBulbConfig->pvMibOccupancyMonitor)
#define PS_MIB_OCCUPANCY_CONTROL   ((tsMibOccupancyControl *)   psMibOccIllBulbConfig->pvMibOccupancyControl)
#define PS_MIB_ILLUMINANCE_STATUS  ((tsMibIlluminanceStatus *)  psMibOccIllBulbConfig->pvMibIlluminanceStatus)
#define PS_MIB_ILLUMINANCE_CONTROL ((tsMibIlluminanceControl *) psMibOccIllBulbConfig->pvMibIlluminanceControl)

/****************************************************************************/
/***        Type Definitions                                              ***/
/****************************************************************************/

/****************************************************************************/
/***        Local Function Prototypes                                     ***/
/****************************************************************************/

/****************************************************************************/
/***        Exported Variables                                            ***/
/****************************************************************************/

/****************************************************************************/
/***        Local Variables                                               ***/
/****************************************************************************/
PRIVATE tsMibOccIllBulbConfig  *psMibOccIllBulbConfig;

/****************************************************************************/
/***        Exported Functions                                            ***/
/****************************************************************************/

/****************************************************************************
 *
 * NAME: MibOccIllBulbConfig_vInit
 *
 * DESCRIPTION:
 * Initialises data
 *
 ****************************************************************************/
PUBLIC void MibOccIllBulbConfig_vInit(thJIP_Mib        hMibOccIllBulbConfigInit,
                         	   tsMibOccIllBulbConfig *psMibOccIllBulbConfigInit,
                         	   void					 *pvMibOccupancyStatusInit,
                         	   void					 *pvMibOccupancyMonitorInit,
                         	   void					 *pvMibOccupancyControlInit,
                         	   void					 *pvMibIlluminanceStatusInit,
                         	   void					 *pvMibIlluminanceControlInit)
{
    /* Debug */
    DBG_vPrintf(DEBUG_OCC_ILL_BULB_CONFIG, "\n%sMibOccIllBulbConfig_vInit() {%d}", acDebugIndent, sizeof(tsMibOccIllBulbConfig));
    Node_vDebugIndent(DEBUG_OCC_ILL_BULB_CONFIG);

    /* Valid data pointer ? */
    if (psMibOccIllBulbConfigInit != (tsMibOccIllBulbConfig *) NULL)
    {
        /* Take copy of pointer to data */
        psMibOccIllBulbConfig = psMibOccIllBulbConfigInit;
        /* Take a copy of the MIB handle */
        psMibOccIllBulbConfig->hMib = hMibOccIllBulbConfigInit;

        /* Take a copy of the other MIB pointers */
		psMibOccIllBulbConfig->pvMibOccupancyStatus    = pvMibOccupancyStatusInit;
		psMibOccIllBulbConfig->pvMibOccupancyMonitor   = pvMibOccupancyMonitorInit;
		psMibOccIllBulbConfig->pvMibOccupancyControl   = pvMibOccupancyControlInit;
		psMibOccIllBulbConfig->pvMibIlluminanceStatus  = pvMibIlluminanceStatusInit;
		psMibOccIllBulbConfig->pvMibIlluminanceControl = pvMibIlluminanceControlInit;

        /* Load Dio mib data */
        (void) PDM_eLoadRecord(&psMibOccIllBulbConfig->sDesc,
							   (uint16)(MIB_ID_OCC_ILL_BULB_CONFIG & 0xFFFF),
                               (void *) &psMibOccIllBulbConfig->sPerm,
                               sizeof(psMibOccIllBulbConfig->sPerm),
                               FALSE);
		/* Debug */
		DBG_vPrintf(DEBUG_OCC_ILL_BULB_CONFIG, "\n%sPDM_eLoadRecord(OccIllBulbConfig) = %d", acDebugIndent, psMibOccIllBulbConfig->sDesc.eState);
    }

	/* Valid LEDs ? */
	#if (DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_LOW != 0 && DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_HIGH != 0)
	{
		/* Configure as outputs */
		vAHI_DioSetDirection(0, (DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_LOW | DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_HIGH));
		vAHI_DioSetPullup(0, (DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_LOW | DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_HIGH));
		/* Turn LEDs off (by turning output on) the tick function will turn them on as appropriate */
		vAHI_DioSetOutput((DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_LOW | DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_HIGH), 0);
	}
	#endif

	/* Debug */
    Node_vDebugOutdent(DEBUG_OCC_ILL_BULB_CONFIG);
}

/****************************************************************************
 *
 * NAME: MibOccIllBulbConfig_vRegister
 *
 * DESCRIPTION:
 * Registers MIB
 *
 ****************************************************************************/
PUBLIC void MibOccIllBulbConfig_vRegister(void)
{
    teJIP_Status eStatus;

    /* Debug */
    DBG_vPrintf(DEBUG_OCC_ILL_BULB_CONFIG, "\n%sMibOccIllBulbConfig_vRegister()", acDebugIndent);
    Node_vDebugIndent(DEBUG_OCC_ILL_BULB_CONFIG);

	/* Address not set in flash ? */
	if (psMibOccIllBulbConfig->sPerm.sAddress.s6_addr32[0] == 0 &&
		psMibOccIllBulbConfig->sPerm.sAddress.s6_addr32[1] == 0 &&
		psMibOccIllBulbConfig->sPerm.sAddress.s6_addr32[2] == 0 &&
		psMibOccIllBulbConfig->sPerm.sAddress.s6_addr32[3] == 0)
	{
		/* Set unique group address */
		Address_vBuildGroup(&psMibOccIllBulbConfig->sPerm.sAddress,
							(MAC_ExtAddr_s *) pvAppApiGetMacAddrLocation(),
							(uint16)(MIB_ID_OCC_ILL_BULB_CONFIG & 0xffff));
	}

    /* Register MIB */
    eStatus = eJIP_RegisterMib(psMibOccIllBulbConfig->hMib);
    /* Debug */
    DBG_vPrintf(DEBUG_OCC_ILL_BULB_CONFIG, "\n%seJIP_RegisterMib(OccIllBulbConfig) = %d", acDebugIndent, eStatus);

    /* Make sure permament data is saved */
    PDM_vSaveRecord(&psMibOccIllBulbConfig->sDesc);
    /* Debug */
    DBG_vPrintf(DEBUG_MIB_OCC_ILL_BULB_CONFIG, "\n%sPDM_vSaveRecord(OccIllBulbConfig) = %d", acDebugIndent, psMibOccIllBulbConfig->sDesc.eState);

	/* Debug */
	Node_vDebugOutdent(DEBUG_OCC_ILL_BULB_CONFIG);
}

/****************************************************************************
 *
 * NAME: MibOccIllBulbConfig_vTick
 *
 * DESCRIPTION:
 * Timer function
 *
 ****************************************************************************/
PUBLIC void MibOccIllBulbConfig_vTick(void)
{
	/* Need to update settings ? */
	if (psMibOccIllBulbConfig->bTxBulb)
	{
		/* Clear flag */
		psMibOccIllBulbConfig->bTxBulb = FALSE;
		/* Reset refresh timer */
		psMibOccIllBulbConfig->u16RefreshTimer = 0;
		/* Is the network running and has been joined ? */
		if (Node_u32StackState() == NODE_STACK_STATE_RUNNING && Node_bJoined())
		{
			ts6LP_SockAddr s6LP_SockAddr;
			teJIP_Status       eResult;

			/* Debug */
			DBG_vPrintf(DEBUG_MIB_OCC_ILL_BULB_CONFIG, "\n%sMibOccIllBulbConfig_vTick()", acDebugIndent);
			Node_vDebugIndent(DEBUG_OCC_ILL_BULB_CONFIG);

			/* Form address */
			s6LP_SockAddr.sin6_family   = E_6LP_PF_INET6;
			s6LP_SockAddr.sin6_flowinfo = 0;
			s6LP_SockAddr.sin6_port     = JIP_DEFAULT_PORT;
			s6LP_SockAddr.sin6_scope_id = 0;
			memcpy(&s6LP_SockAddr.sin6_addr, &psMibOccIllBulbConfig->sPerm.sAddress, sizeof(in6_addr));
			/* Set TX LED on */
			vAHI_DioSetOutput(DEVICE_OUTPUT_MASK_TX, 0);
			/* Transmit mode */
			eResult = eJIP_Remote_Mib_Set(&s6LP_SockAddr, 0, MIB_ID_BULB_CONTROL, VAR_IX_BULB_CONTROL_MODE, E_JIP_VAR_TYPE_UINT8, &psMibOccIllBulbConfig->u8BulbControlMode, 1);
			/* Debug */
			DBG_vPrintf(DEBUG_MIB_OCC_ILL_BULB_CONFIG, "\n%seJIP_Remote_Mib_Set(0x%x, %d, %d) = %d }",
					acDebugIndent, MIB_ID_BULB_CONTROL, VAR_IX_BULB_CONTROL_MODE, psMibOccIllBulbConfig->u8BulbControlMode,	eResult);
			/* UART ? */
			#ifdef UART_H_INCLUDED
			{
				/* Initialise UART */
				UART_vChar(' ');
				UART_vChar('t');
				if (eResult != E_JIP_OK)
				{
					UART_vString("=0x");
					UART_vNumber(eResult, 16);
					UART_vString("/0x");
					UART_vNumber(u32_6LP_GetErrNo(), 16);
				}
				UART_vChar(' ');
			}
			#endif
			/* Set TX LED on */
			vAHI_DioSetOutput(DEVICE_OUTPUT_MASK_TX, 0);
			/* Transmit target level */
			eResult = eJIP_Remote_Mib_Set(&s6LP_SockAddr, 0, MIB_ID_BULB_CONTROL, VAR_IX_BULB_CONTROL_LUM_TARGET, E_JIP_VAR_TYPE_UINT8, &psMibOccIllBulbConfig->u8BulbControlLumTarget, 1);
			/* Debug */
			DBG_vPrintf(DEBUG_MIB_OCC_ILL_BULB_CONFIG, "\n%seJIP_Remote_Mib_Set(0x%x, %d, %d) = %d }",
					acDebugIndent, MIB_ID_BULB_CONTROL, VAR_IX_BULB_CONTROL_LUM_TARGET, psMibOccIllBulbConfig->u8BulbControlLumTarget, eResult);
			/* UART ? */
			#ifdef UART_H_INCLUDED
			{
				/* Initialise UART */
				UART_vChar(' ');
				UART_vChar('t');
				if (eResult != E_JIP_OK)
				{
					UART_vString("=0x");
					UART_vNumber(eResult, 16);
					UART_vString("/0x");
					UART_vNumber(u32_6LP_GetErrNo(), 16);
				}
				UART_vChar(' ');
			}
			#endif
			Node_vDebugOutdent(DEBUG_OCC_ILL_BULB_CONFIG);
		}
	}
}

/****************************************************************************
 *
 * NAME: MibOccIllBulbConfig_vAppTimer100ms
 *
 * DESCRIPTION:
 * Called once every 100ms
 *
 ****************************************************************************/
PUBLIC void MibOccIllBulbConfig_vAppTimer100ms(void)
{
	bool_t bOccupancyEnabled   = FALSE;
	bool_t bIlluminanceEnabled = FALSE;
	bool_t u8Occupancy		   = 0;

	//DBG_vPrintf(DEBUG_DEVICE_FUNC, "\nT");
	/* Do we have valid pointers to local occupancy MIBs ? */
	if (PS_MIB_OCCUPANCY_STATUS  != (tsMibOccupancyStatus *) NULL &&
		PS_MIB_OCCUPANCY_CONTROL != (tsMibOccupancyControl *) NULL)
	{
		/* Is the sensor enabled ? */
		if (VAR_VAL_OCCUPANCY_CONTROL_MODE_OFF != PS_MIB_OCCUPANCY_CONTROL->sPerm.u8Mode)
		{
			/* Is this MIB configured to react to occupancy changes ? */
			if (VAR_VAL_OCC_ILL_BULB_CONFIG_MODE_OCCUPANCY    == psMibOccIllBulbConfig->sPerm.u8Mode ||
				VAR_VAL_OCC_ILL_BULB_CONFIG_MODE_OCC_ILL_AUTO == psMibOccIllBulbConfig->sPerm.u8Mode ||
				VAR_VAL_OCC_ILL_BULB_CONFIG_MODE_OCC_ILL_MAX  == psMibOccIllBulbConfig->sPerm.u8Mode)
			{
				/* Enable occupancy processing */
				bOccupancyEnabled = TRUE;
				//DBG_vPrintf(DEBUG_DEVICE_FUNC, "O");
				/* Is the local sensor reporting occupied ? */
				if (PS_MIB_OCCUPANCY_STATUS->sTemp.u8Occupancy != 0)
				{
					/* Flag that we are occupied */
					u8Occupancy = 1;
				}
			}
		}
	}
	/* Do we have valid pointers to remote occupancy MIBs ? */
	if (PS_MIB_OCCUPANCY_MONITOR != (tsMibOccupancyMonitor *) NULL)
	{
		/* Is the sensor enabled ? */
		if (VAR_VAL_OCCUPANCY_MONITOR_MODE_OFF != PS_MIB_OCCUPANCY_MONITOR->sPerm.u8Mode)
		{
			/* Is this MIB configured to react to occupancy changes ? */
			if (VAR_VAL_OCC_ILL_BULB_CONFIG_MODE_OCCUPANCY    == psMibOccIllBulbConfig->sPerm.u8Mode ||
				VAR_VAL_OCC_ILL_BULB_CONFIG_MODE_OCC_ILL_AUTO == psMibOccIllBulbConfig->sPerm.u8Mode ||
				VAR_VAL_OCC_ILL_BULB_CONFIG_MODE_OCC_ILL_MAX  == psMibOccIllBulbConfig->sPerm.u8Mode)
			{
				/* Enable occupancy processing */
				bOccupancyEnabled = TRUE;
				//DBG_vPrintf(DEBUG_DEVICE_FUNC, "P");
				/* Is the local sensor reporting occupied ? */
				if ((PS_MIB_OCCUPANCY_MONITOR->sTemp.u32Enabled & PS_MIB_OCCUPANCY_MONITOR->sTemp.u32Occupied) != 0)
				{
					/* Flag that we are occupied */
					u8Occupancy = 1;
				}
			}
		}
	}

	/* Do we have valid pointers to illuminance MIBs ? */
	if (PS_MIB_ILLUMINANCE_STATUS  != (tsMibIlluminanceStatus *) NULL &&
		PS_MIB_ILLUMINANCE_CONTROL != (tsMibIlluminanceControl *) NULL)
	{
		/* Is the sensor enabled ? */
		if (VAR_VAL_ILLUMINANCE_CONTROL_MODE_OFF != PS_MIB_ILLUMINANCE_CONTROL->sPerm.u8Mode)
		{
			/* Is this MIB configured to react to illuminance changes ? */
			if (VAR_VAL_OCC_ILL_BULB_CONFIG_MODE_ILLUMINANCE  == psMibOccIllBulbConfig->sPerm.u8Mode ||
				VAR_VAL_OCC_ILL_BULB_CONFIG_MODE_OCC_ILL_AUTO == psMibOccIllBulbConfig->sPerm.u8Mode ||
				VAR_VAL_OCC_ILL_BULB_CONFIG_MODE_OCC_ILL_MAX  == psMibOccIllBulbConfig->sPerm.u8Mode)
			{
				/* Enable occupancy processing */
				bIlluminanceEnabled = TRUE;
				//DBG_vPrintf(DEBUG_DEVICE_FUNC, "I");
			}
		}
	}

	/* Is occupancy or illuminance control enabled ? */
	if (bOccupancyEnabled || bIlluminanceEnabled)
	{
		/* Is adjustment interval set ? */
		if (psMibOccIllBulbConfig->sPerm.u16AdjustInterval > 0)
		{
			/* Increment adjustment timer */
			if (psMibOccIllBulbConfig->u16AdjustTimer < (0xffff - DEVICE_WAKE_TIMER_PERIOD_10MS))
			{
				//DBG_vPrintf(DEBUG_DEVICE_FUNC, " a=%d ++", psMibOccIllBulbConfig->u16AdjustTimer);
				psMibOccIllBulbConfig->u16AdjustTimer += DEVICE_WAKE_TIMER_PERIOD_10MS;
			}
			//DBG_vPrintf(DEBUG_DEVICE_FUNC, " %d>=%d ", psMibOccIllBulbConfig->u16AdjustTimer, psMibOccIllBulbConfig->sPerm.u16AdjustInterval);
			/* Time to check readings and transmit adjustments ? */
			if (psMibOccIllBulbConfig->u16AdjustTimer >= psMibOccIllBulbConfig->sPerm.u16AdjustInterval)
			{
				//DBG_vPrintf(DEBUG_DEVICE_FUNC, "\nA");//, O=%d && I=%d", bOccupancyEnabled, bIlluminanceEnabled);
				/* Reset timer */
				psMibOccIllBulbConfig->u16AdjustTimer = 0;
				/* Are both the occupancy and illuminance enabled ? */
				if (bOccupancyEnabled && bIlluminanceEnabled)
				{
					//DBG_vPrintf(DEBUG_DEVICE_FUNC, "OI");//, O=%d && I=%d", bOccupancyEnabled, bIlluminanceEnabled);
					//DBG_vPrintf(DEBUG_DEVICE_FUNC, " OR%d!=%d && IR%d!=%d",
					//    psMibOccIllBulbConfig->u32OccupancyReading,   PS_MIB_OCCUPANCY_STATUS->u32Reading,
					//	psMibOccIllBulbConfig->u32IlluminanceReading, PS_MIB_ILLUMINANCE_STATUS->u32Reading);
					/* Is a fresh illuminance sensor reading available ? */
					if (psMibOccIllBulbConfig->u32IlluminanceReading != PS_MIB_ILLUMINANCE_STATUS->u32Reading)
					{
						//DBG_vPrintf(DEBUG_DEVICE_FUNC, "R");//, O=%d && I=%d", bOccupancyEnabled, bIlluminanceEnabled);
						/* Note new reading */
						psMibOccIllBulbConfig->u32IlluminanceReading = PS_MIB_ILLUMINANCE_STATUS->u32Reading;
						/* Unoccupied ? */
						if (0 == u8Occupancy)
						{
							/* Does occupancy processing indicate we need to send an update ? */
							if (MibOccIllBulbConfig_bOccupancy(u8Occupancy))
							{
								/* Queue update */
								MibOccIllBulbConfig_vQueueTxBulb(TRUE);
							}
						}
						else
						{
							/* Maximising brightness when occupied and dark ? */
							if (VAR_VAL_OCC_ILL_BULB_CONFIG_MODE_OCC_ILL_MAX  == psMibOccIllBulbConfig->sPerm.u8Mode)
							{
								/* Update for maximum illuminance */
								if (MibOccIllBulbConfig_bIlluminanceMax(PS_MIB_ILLUMINANCE_STATUS->sTemp.u8TargetStatus))
								{
									/* Queue update */
									MibOccIllBulbConfig_vQueueTxBulb(TRUE);
								}
							}
							/* Trying to hit target when occupied ? */
							else
							{
								/* Update for auto illuminance */
								if (MibOccIllBulbConfig_bIlluminanceAuto(PS_MIB_ILLUMINANCE_STATUS->sTemp.u8TargetStatus))
								{
									/* Queue update */
									MibOccIllBulbConfig_vQueueTxBulb(TRUE);
								}
							}
						}
					}
				}
				/* Just occupancy is enabled ? */
				else if (bOccupancyEnabled)
				{
					/* Update for occupancy */
					if (MibOccIllBulbConfig_bOccupancy(u8Occupancy))
					{
						/* Queue update */
						MibOccIllBulbConfig_vQueueTxBulb(TRUE);
					}
				}
				/* Just illuminance is enabled ? */
				else if (bIlluminanceEnabled)
				{
					/* New reading from illuminance sensor available ? */
					if (psMibOccIllBulbConfig->u32IlluminanceReading != PS_MIB_ILLUMINANCE_STATUS->u32Reading)
					{
						/* Debug */
						DBG_vPrintf(DEBUG_MIB_OCC_ILL_BULB_CONFIG, "\n%sMibOccIllBulbConfig_vAppTimer100ms(NEW ILLUMINANCE READING)", acDebugIndent);
						/* Note new readings */
						psMibOccIllBulbConfig->u32IlluminanceReading = PS_MIB_ILLUMINANCE_STATUS->u32Reading;
						/* Maximising brightness when dark ? */
						if (VAR_VAL_OCC_ILL_BULB_CONFIG_MODE_OCC_ILL_MAX  == psMibOccIllBulbConfig->sPerm.u8Mode)
						{
							/* Update for maximum illuminance */
							if(MibOccIllBulbConfig_bIlluminanceMax(PS_MIB_ILLUMINANCE_STATUS->sTemp.u8TargetStatus))
							{
								/* Queue update */
								MibOccIllBulbConfig_vQueueTxBulb(TRUE);
							}
						}
						/* Trying to hit target when occupied ? */
						else
						{
							/* Update for auto illuminance */
							if (MibOccIllBulbConfig_bIlluminanceAuto(PS_MIB_ILLUMINANCE_STATUS->sTemp.u8TargetStatus))
							{
								/* Queue update */
								MibOccIllBulbConfig_vQueueTxBulb(TRUE);
							}
						}
					}
				}
			}
		}

		/* Update refresh interval */
		MibOccIllBulbConfig_vRefreshIntervalUpdate();
	}
	/* Always off mode ? */
	else if (VAR_VAL_OCC_ILL_BULB_CONFIG_MODE_ALWAYS_OFF  == psMibOccIllBulbConfig->sPerm.u8Mode)
	{
		/* Want bulbs on at maximum level */
		psMibOccIllBulbConfig->u8BulbControlMode      = VAR_VAL_BULB_CONTROL_MODE_OFF;
		psMibOccIllBulbConfig->u8BulbControlLumTarget = 255;
		/* Mode changed - force an update */
		if (psMibOccIllBulbConfig->u8ModeTick != psMibOccIllBulbConfig->sPerm.u8Mode) psMibOccIllBulbConfig->bTxBulb = TRUE;
		/* Update refresh interval */
		MibOccIllBulbConfig_vRefreshIntervalUpdate();
	}
	/* Always on mode ? */
	else if (VAR_VAL_OCC_ILL_BULB_CONFIG_MODE_ALWAYS_ON  == psMibOccIllBulbConfig->sPerm.u8Mode)
	{
		/* Want bulbs on at maximum level */
		psMibOccIllBulbConfig->u8BulbControlMode      = VAR_VAL_BULB_CONTROL_MODE_ON;
		psMibOccIllBulbConfig->u8BulbControlLumTarget = 255;
		/* Mode changed - force an update */
		if (psMibOccIllBulbConfig->u8ModeTick != psMibOccIllBulbConfig->sPerm.u8Mode) psMibOccIllBulbConfig->bTxBulb = TRUE;
		/* Update refresh interval */
		MibOccIllBulbConfig_vRefreshIntervalUpdate();
	}
	/* Nothing is enabled ? */
	else
	{
		//DBG_vPrintf(DEBUG_DEVICE_FUNC, "D");
		/* Maximise timers to ensure readings are taken when sensors are enabled again */
		psMibOccIllBulbConfig->u16AdjustTimer  = 0xffff;
		psMibOccIllBulbConfig->u16RefreshTimer = 0xffff;
		/* Valid LEDs ? */
		#if (DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_LOW != 0 && DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_HIGH != 0)
		{
			/* Turn low and high LEDs off */
			vAHI_DioSetOutput(0, (DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_LOW | DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_HIGH));
		}
		#endif
	}

	/* Note mode */
	psMibOccIllBulbConfig->u8ModeTick = psMibOccIllBulbConfig->sPerm.u8Mode;
}
/****************************************************************************
 *
 * NAME: MibOccIllBulbConfig_bOccupancy
 *
 * DESCRIPTION:
 * Updates settings dependant upon motion
 *
 ****************************************************************************/
PUBLIC bool_t MibOccIllBulbConfig_bOccupancy(uint8 u8Occupancy)
{
	bool_t bReturn = FALSE;

	/* Occupied ? */
	if (0 != u8Occupancy)
	{
		/* Are bulbs currently off ? */
		if (psMibOccIllBulbConfig->u8BulbControlMode != VAR_VAL_BULB_CONTROL_MODE_ON)
		{
			/* Want bulbs on at maximum level */
			psMibOccIllBulbConfig->u8BulbControlMode      = VAR_VAL_BULB_CONTROL_MODE_ON;
			psMibOccIllBulbConfig->u8BulbControlLumTarget = 255;
			/* Debug */
			DBG_vPrintf(DEBUG_MIB_OCC_ILL_BULB_CONFIG, "\n%sMibOccIllBulbConfig_bOccupancy(%d)", acDebugIndent, u8Occupancy);
			/* Return updated */
			bReturn = TRUE;
		}
		/* Valid LEDs ? */
		#if (DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_LOW != 0 && DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_HIGH != 0)
		{
			/* Turn low and high LEDs on */
			vAHI_DioSetOutput(0, (DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_LOW | DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_HIGH));
		}
		#endif
	}
	/* Unoccupied ? */
	else
	{
		/* Are bulbs currently on ? */
		if (psMibOccIllBulbConfig->u8BulbControlMode == VAR_VAL_BULB_CONTROL_MODE_ON)
		{
			/* Want bulbs off at maximum level */
			psMibOccIllBulbConfig->u8BulbControlMode      = VAR_VAL_BULB_CONTROL_MODE_OFF;
			//psMibOccIllBulbConfig->u8BulbControlLumTarget = 255;
			/* Debug */
			DBG_vPrintf(DEBUG_MIB_OCC_ILL_BULB_CONFIG, "\n%sMibOccIllBulbConfig_bOccupancy(%d)", acDebugIndent, u8Occupancy);
			/* Return updated */
			bReturn = TRUE;
		}
		/* Valid LEDs ? */
		#if (DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_LOW != 0 && DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_HIGH != 0)
		{
			/* Turn low and high LEDs off */
			vAHI_DioSetOutput((DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_LOW | DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_HIGH), 0);
		}
		#endif
	}
	//DBG_vPrintf(DEBUG_DEVICE_FUNC, "\no(%d)=%d ", u8Occupancy, bReturn);

	return bReturn;
}

/****************************************************************************
 *
 * NAME: MibOccIllBulbConfig_bIlluminanceAuto
 *
 * DESCRIPTION:
 * Updates settings dependant upon light
 *
 ****************************************************************************/
PUBLIC bool_t MibOccIllBulbConfig_bIlluminanceAuto(uint8 u8TargetStatus)
{
	bool_t bReturn = FALSE;
	char  asState[4][5] = {"dis", "ok", "low", "high"};

	/* Is the light level too low ? */
	if (u8TargetStatus == VAR_VAL_ILLUMINANCE_STATUS_TARGET_STATUS_LOW)
	{
		/* Are bulbs currently not on ? */
		if (psMibOccIllBulbConfig->u8BulbControlMode != VAR_VAL_BULB_CONTROL_MODE_ON)
		{
			/* Turn them on */
			psMibOccIllBulbConfig->u8BulbControlMode = VAR_VAL_BULB_CONTROL_MODE_ON;
			/* Return updated */
			bReturn = TRUE;
		}
		/* Can the bulbs be brightened ? */
		else if (psMibOccIllBulbConfig->u8BulbControlLumTarget < 255)
		{
			/* Want bulbs on */
			psMibOccIllBulbConfig->u8BulbControlMode = VAR_VAL_BULB_CONTROL_MODE_ON;
			/* Increase brightness */
			if (psMibOccIllBulbConfig->u8BulbControlLumTarget < 255 - psMibOccIllBulbConfig->sPerm.u8LuminanceDelta)
				psMibOccIllBulbConfig->u8BulbControlLumTarget += psMibOccIllBulbConfig->sPerm.u8LuminanceDelta;
			else
				psMibOccIllBulbConfig->u8BulbControlLumTarget = 255;
			/* Return updated */
			bReturn = TRUE;
		}
		/* Valid LEDs ? */
		#if (DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_LOW != 0 && DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_HIGH != 0)
		{
			/* Turn low LED on, high LED off */
			vAHI_DioSetOutput(DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_LOW, DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_HIGH);
		}
		#endif
	}
	/* Is the light level too high ? */
	else if (u8TargetStatus == VAR_VAL_ILLUMINANCE_STATUS_TARGET_STATUS_HIGH)
	{
		/* Are bulbs currently on ? */
		if (psMibOccIllBulbConfig->u8BulbControlMode == VAR_VAL_BULB_CONTROL_MODE_ON)
		{
			/* Can the bulbs be dimmed ? */
			if (psMibOccIllBulbConfig->u8BulbControlLumTarget > 0)
			{
				/* Want bulbs on */
				psMibOccIllBulbConfig->u8BulbControlMode = VAR_VAL_BULB_CONTROL_MODE_ON;
				/* Decrease brightness */
				if (psMibOccIllBulbConfig->u8BulbControlLumTarget > psMibOccIllBulbConfig->sPerm.u8LuminanceDelta)
					psMibOccIllBulbConfig->u8BulbControlLumTarget -= psMibOccIllBulbConfig->sPerm.u8LuminanceDelta;
				else
					psMibOccIllBulbConfig->u8BulbControlLumTarget = 0;
				/* Return updated */
				bReturn = TRUE;
			}
			/* Can't dim any further ? */
			else
			{
				/* Turn them off */
				psMibOccIllBulbConfig->u8BulbControlMode = VAR_VAL_BULB_CONTROL_MODE_OFF;
				/* Return updated */
				bReturn = TRUE;
			}
		}
		/* Valid LEDs ? */
		#if (DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_LOW != 0 && DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_HIGH != 0)
		{
			/* Turn high LED on, low LED off */
			vAHI_DioSetOutput(DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_HIGH, DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_LOW);
		}
		#endif
	}
	/* Is the light level ok ? */
	else if (u8TargetStatus == VAR_VAL_ILLUMINANCE_STATUS_TARGET_STATUS_OK)
	{
		/* Valid LEDs ? */
		#if (DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_LOW != 0 && DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_HIGH != 0)
		{
			/* Turn low and high LEDs on */
			vAHI_DioSetOutput(0, (DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_LOW | DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_HIGH));
		}
		#endif
	}
	/* Other light levels (disabled) ? */
	else
	{
		/* Valid LEDs ? */
		#if (DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_LOW != 0 && DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_HIGH != 0)
		{
			/* Turn low and high LEDs off */
			vAHI_DioSetOutput((DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_LOW | DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_HIGH), 0);
		}
		#endif
	}
	DBG_vPrintf(DEBUG_DEVICE_FUNC, "\ni(%s)=%d ", asState[u8TargetStatus], bReturn);

	return bReturn;
}

/****************************************************************************
 *
 * NAME: MibOccIllBulbConfig_bIlluminanceMax
 *
 * DESCRIPTION:
 * Updates settings dependant upon light
 *
 ****************************************************************************/
PUBLIC bool_t MibOccIllBulbConfig_bIlluminanceMax(uint8 u8TargetStatus)
{
	bool_t bReturn = FALSE;
	char  asState[4][5] = {"dis", "ok", "low", "high"};

	/* Is the light level too low ? */
	if (u8TargetStatus == VAR_VAL_ILLUMINANCE_STATUS_TARGET_STATUS_LOW)
	{
		/* Are bulbs currently not on ? */
		if (psMibOccIllBulbConfig->u8BulbControlMode != VAR_VAL_BULB_CONTROL_MODE_ON)
		{
			/* Turn them on */
			psMibOccIllBulbConfig->u8BulbControlMode = VAR_VAL_BULB_CONTROL_MODE_ON;
			/* Maximise brightness */
			psMibOccIllBulbConfig->u8BulbControlLumTarget  = 255;
			/* Return updated */
			bReturn = TRUE;
		}
		/* Valid LEDs ? */
		#if (DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_LOW != 0 && DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_HIGH != 0)
		{
			/* Turn low LED on, high LED off */
			vAHI_DioSetOutput(DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_LOW, DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_HIGH);
		}
		#endif
	}
	/* Is the light level too high ? */
	else if (u8TargetStatus == VAR_VAL_ILLUMINANCE_STATUS_TARGET_STATUS_HIGH)
	{
		/* Are bulbs currently not off ? */
		if (psMibOccIllBulbConfig->u8BulbControlMode != VAR_VAL_BULB_CONTROL_MODE_OFF)
		{
			/* Turn them off */
			psMibOccIllBulbConfig->u8BulbControlMode = VAR_VAL_BULB_CONTROL_MODE_OFF;
			/* Maximise brightness */
			psMibOccIllBulbConfig->u8BulbControlLumTarget  = 255;
			/* Return updated */
			bReturn = TRUE;
		}
		/* Valid LEDs ? */
		#if (DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_LOW != 0 && DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_HIGH != 0)
		{
			/* Turn high LED on, low LED off */
			vAHI_DioSetOutput(DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_HIGH, DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_LOW);
		}
		#endif
	}
	/* Is the light level ok ? */
	else if (u8TargetStatus == VAR_VAL_ILLUMINANCE_STATUS_TARGET_STATUS_OK)
	{
		/* Valid LEDs ? */
		#if (DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_LOW != 0 && DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_HIGH != 0)
		{
			/* Turn low and high LEDs on */
			vAHI_DioSetOutput(0, (DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_LOW | DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_HIGH));
		}
		#endif
	}
	/* Other light levels (disabled) ? */
	else
	{
		/* Valid LEDs ? */
		#if (DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_LOW != 0 && DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_HIGH != 0)
		{
			/* Turn low and high LEDs off */
			vAHI_DioSetOutput((DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_LOW | DEVICE_OUTPUT_MASK_OCC_ILL_BULB_CONFIG_HIGH), 0);
		}
		#endif
	}
	DBG_vPrintf(DEBUG_DEVICE_FUNC, "\ni(%s)=%d ", asState[u8TargetStatus], bReturn);

	return bReturn;
}

/****************************************************************************
 *
 * NAME: MibOccIllBulbConfig_vRefreshIntervalUpdate
 *
 * DESCRIPTION:
 * Updates and checks refresh interval
 *
 ****************************************************************************/
PUBLIC void MibOccIllBulbConfig_vRefreshIntervalUpdate(void)
{
	/* Refresh interval is set ? */
	if (psMibOccIllBulbConfig->sPerm.u16RefreshInterval > 0)
	{
		/* Increment refresh timer */
		if (psMibOccIllBulbConfig->u16RefreshTimer < 0xffff) psMibOccIllBulbConfig->u16RefreshTimer += DEVICE_WAKE_TIMER_PERIOD_10MS;
		/* Time to check readings and transmit adjustments ? */
		if (psMibOccIllBulbConfig->u16RefreshTimer >= psMibOccIllBulbConfig->sPerm.u16RefreshInterval)
		{
			/* Debug */
			DBG_vPrintf(DEBUG_MIB_OCC_ILL_BULB_CONFIG, "\n%sMibOccIllBulbConfig_vRefreshIntervalUpdate()", acDebugIndent);
			Node_vDebugIndent(DEBUG_MIB_OCC_ILL_BULB_CONFIG);
			/* Zero refresh timer */
			psMibOccIllBulbConfig->u16RefreshTimer = 0;
			/* Queue transmission of bulb control commands */
			MibOccIllBulbConfig_vQueueTxBulb(FALSE);
			/* Debug */
			Node_vDebugOutdent(DEBUG_MIB_OCC_ILL_BULB_CONFIG);
		}
	}
}

/****************************************************************************
 *
 * NAME: MibOccIllBulbConfig_vQueueTxBulb
 *
 * DESCRIPTION:
 * Queues a bulb control transmission
 *
 ****************************************************************************/
PUBLIC void MibOccIllBulbConfig_vQueueTxBulb(bool_t bImmediate)
{
	/* Have we joined a network ? */
	if (Node_bJoined())
	{
		/* End device build ? */
		#ifdef MK_BLD_NODE_TYPE_END_DEVICE
		{
			/* Is the stack not running ? */
			if (Node_u32StackState() != NODE_STACK_STATE_RUNNING)
			{
				/* Immediate transmission ? */
				if (bImmediate)
				{
					/* Resume running the stack */
					Node_v6lpResume();
				}
			}
			/* Debug */
			DBG_vPrintf(DEBUG_MIB_OCC_ILL_BULB_CONFIG, "\n%sMibOccIllBulbConfig_vQueueTxBulb() { bTxBulb = TRUE }", acDebugIndent);
			/* Flag bulb control transmission */
			psMibOccIllBulbConfig->bTxBulb = TRUE;
			/* UART ? */
			#ifdef UART_H_INCLUDED
			{
				/* Initialise UART */
				UART_vChar('q');
			}
			#endif
		}
		/* Coordinator or router build ? */
		#else
		{
			/* Stack is running ? */
			if (Node_u32StackState() == NODE_STACK_STATE_RUNNING)
			{
				/* Debug */
				DBG_vPrintf(DEBUG_MIB_OCC_ILL_BULB_CONFIG, "\n%sMibOccIllBulbConfig_vQueueTxBulb() { bTxBulb = TRUE }", acDebugIndent);
				/* Flag bulb control transmission */
				psMibOccIllBulbConfig->bTxBulb = TRUE;
			}
		}
		#endif
	}
}

/****************************************************************************
 *
 * NAME: MibOccIllBulbConfig_eSetUint8
 *
 * DESCRIPTION:
 * Generic set data callback
 *
 ****************************************************************************/
PUBLIC teJIP_Status MibOccIllBulbConfig_eSetUint8(uint8 u8Val, void *pvCbData)
{
	teJIP_Status eReturn;

	/* Call standard function */
	eReturn = eSetUint8(u8Val, pvCbData);

    /* Make sure permament data is saved */
    PDM_vSaveRecord(&psMibOccIllBulbConfig->sDesc);
    /* Debug */
    DBG_vPrintf(DEBUG_MIB_OCC_ILL_BULB_CONFIG, "\n%sPDM_vSaveRecord(OccIllBulbConfig) = %d", acDebugIndent, psMibOccIllBulbConfig->sDesc.eState);

	return eReturn;
}

/****************************************************************************
 *
 * NAME: MibOccIllBulbConfig_eSetUint16
 *
 * DESCRIPTION:
 * Generic set data callback
 *
 ****************************************************************************/
PUBLIC teJIP_Status MibOccIllBulbConfig_eSetUint16(uint16 u16Val, void *pvCbData)
{
	teJIP_Status eReturn;

	/* Call standard function */
	eReturn = eSetUint16(u16Val, pvCbData);

    /* Make sure permament data is saved */
    PDM_vSaveRecord(&psMibOccIllBulbConfig->sDesc);
    /* Debug */
    DBG_vPrintf(DEBUG_MIB_OCC_ILL_BULB_CONFIG, "\n%sPDM_vSaveRecord(OccIllBulbConfig) = %d", acDebugIndent, psMibOccIllBulbConfig->sDesc.eState);

	return eReturn;
}

/****************************************************************************
 *
 * NAME: MibOccIllBulbConfig_eSetAddr
 *
 * DESCRIPTION:
 * Handle remote set of a group address
 *
 ****************************************************************************/
PUBLIC teJIP_Status MibOccIllBulbConfig_eSetAddress(const uint8 *pu8Val, uint8 u8Len, void *pvCbData)

{
	teJIP_Status eReturn;

	/* Call helper function */
	eReturn = Address_eSet(pu8Val, u8Len, pvCbData);

	/* Success ? */
	if (eReturn == E_JIP_OK)
	{
	    /* Make sure permament data is saved */
	    PDM_vSaveRecord(&psMibOccIllBulbConfig->sDesc);
	    /* Debug */
	    DBG_vPrintf(DEBUG_MIB_OCC_ILL_BULB_CONFIG, "\n%sPDM_vSaveRecord(OccIllBulbConfig) = %d", acDebugIndent, psMibOccIllBulbConfig->sDesc.eState);
	}

	return eReturn;
}

/****************************************************************************/
/***        END OF FILE                                                   ***/
/****************************************************************************/
