o
    Li9                     @   s`  d Z ddlZddlZddlmZmZmZmZmZ ddl	m
Z
mZmZmZ edeZe
eeeejjdZdd Zd	d
 Zejddgddd Zejdddgddd Zejdddgddd Zejdddgddd Zeddd Zeddd Zejd dd!gdd"d# Zejd$d%gdd&d' Zed(d)d* Zejd+d%gdd,d- ZdS ).zK
Moisture Meter: receives readings from ESP32, stores in MySQL shop_stats.
    N)	Blueprintrequestjsonifyrender_templateResponse)
MYSQL_HOST
MYSQL_USERMYSQL_PASSWORDMYSQL_DATABASEmoisture)hostuserpassworddatabasecursorclassc                   C   s   t jdi tS )N )pymysqlconnect	DB_CONFIGr   r   r   +/var/www/html/Server/blueprints/moisture.pyget_db   s   r   c              	   C   s   |   l}|d z|d W n
 tjy   Y nw z|d W n
 tjy-   Y nw z|d W n
 tjy?   Y nw |d z|d W n
 tjyV   Y nw z|d W n
 tjyh   Y nw W d    n1 ssw   Y  |   d S )Na  
            CREATE TABLE IF NOT EXISTS MoistureLevel (
                id INT AUTO_INCREMENT PRIMARY KEY,
                device_id VARCHAR(16) NOT NULL,
                node_name VARCHAR(50) DEFAULT NULL,
                moisture INT NOT NULL,
                battery_voltage FLOAT NOT NULL,
                command VARCHAR(20) DEFAULT 'sleep',
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
            )
        zHALTER TABLE MoistureLevel ADD COLUMN command VARCHAR(20) DEFAULT 'sleep'zGALTER TABLE MoistureLevel ADD COLUMN node_name VARCHAR(50) DEFAULT NULLzNALTER TABLE MoistureLevel ADD COLUMN firmware_version VARCHAR(24) DEFAULT NULLa/  
            CREATE TABLE IF NOT EXISTS device_command (
                device_id VARCHAR(16) PRIMARY KEY,
                command VARCHAR(20) NOT NULL DEFAULT 'sleep',
                new_device_id VARCHAR(16) DEFAULT NULL,
                new_node_name VARCHAR(50) DEFAULT NULL
            )
        zLALTER TABLE device_command ADD COLUMN new_device_id VARCHAR(16) DEFAULT NULLzLALTER TABLE device_command ADD COLUMN new_node_name VARCHAR(50) DEFAULT NULL)cursorexecuter   OperationalErrorcommit)conncurr   r   r   ensure_tables   s>   


(r   z/readingPOST)methodsc               
   C   sN  ztt jddd} | stddddfW S | d}| d}| d	}| d
}| d}|d u s:|d u s:|d u rDtddddfW S t|trM| sWtddddfW S | d d }t|trf|nd d d pod }zt|}W n tt	fy   tddddf Y W S w zt
|}W n tt	fy   tddddf Y W S w t|tr|nd d d pd }t }zt| | }|d|f | }|r|d nd  }	|	dvrd}	|r|dpd d d nd }
|r|dpd d d nd }|d|||||	|f |
r|
n|}|r|n|p"d}|
r-|d|f |r7|d|f |	dkrC|d|f W d    n	1 sNw   Y  |  W |  n|  w tjd|	||d d!d"}t|d#d$d%W S  tjy   tdd&dd'f Y S  ty } ztdt|dd'fW  Y d }~S d }~ww )(NTforcesilentFzInvalid or missing JSONokerror  	device_id	node_namer   battery_voltagefirmware_versionz/Missing device_id, moisture, or battery_voltagez$device_id must be a non-empty string    2   zmoisture must be a numberz battery_voltage must be a number   USELECT command, new_device_id, new_node_name FROM device_command WHERE device_id = %scommandnosleepsleepr1   rebootnew_device_idnew_node_namezINSERT INTO MoistureLevel (device_id, node_name, moisture, battery_voltage, command, firmware_version) VALUES (%s, %s, %s, %s, %s, %s)NodezCUPDATE device_command SET new_device_id = NULL WHERE device_id = %szCUPDATE device_command SET new_node_name = NULL WHERE device_id = %sr4   zBUPDATE device_command SET command = 'nosleep' WHERE device_id = %s)r$   r0   r'   r(   ),:)
separators   zapplication/json)statusmimetypeDatabase error  )r   get_jsonr   get
isinstancestrstripint	TypeError
ValueErrorfloatr   r   r   r   fetchonelowerr   closejsondumpsr   r   Error	Exception)datar'   r(   r   r)   r*   r   r   rowcurrent_commandr5   r6   resp_device_idresp_node_namebodyer   r   r   readingE   s   




""
"$

!"rW   z/device/<device_id>/commandPUTc              
   C   sH  ztt jdddp	i }|dpd  }|dvr#tdddd	fW S |  d
d } | s7tdddd	fW S t }z+t| | }|	d| ||f W d
   n1 sWw   Y  |
  W |  n|  w td|ddfW S  tjy   tddddf Y S  ty } ztdt|ddfW  Y d
}~S d
}~ww )zESet command for device: body {"command": "sleep"|"nosleep"|"reboot"}.Tr    r0   r,   r2   Fz/command must be 'sleep', 'nosleep', or 'reboot'r#   r&   Nr+   invalid device_idzdINSERT INTO device_command (device_id, command) VALUES (%s, %s) ON DUPLICATE KEY UPDATE command = %s)r$   r0   r;   r>   r?   )r   r@   rA   rD   rJ   r   r   r   r   r   r   rK   r   rN   rO   rC   )r'   rP   cmdr   r   rV   r   r   r   set_command   s4   

"r[   z/device/<device_id>/device_idc              
   C   sJ  zut jdddp	i }|dpd dd }|s#tddd	d
fW S |  dd } | s7tddd	d
fW S t }z+t| | }|d| ||f W d   n1 sWw   Y  |	  W |
  n|
  w td| |ddfW S  tjy   tddd	df Y S  ty } ztdt|d	dfW  Y d}~S d}~ww )zVAssign new device ID: body {"device_id": "M001"}. Device will pick it up on next POST.Tr    r'   r,   Nr+   Fzdevice_id requiredr#   r&   invalid device_id in URLzINSERT INTO device_command (device_id, command, new_device_id) VALUES (%s, 'nosleep', %s) ON DUPLICATE KEY UPDATE new_device_id = %s)r$   
current_idnew_idr;   r>   r?   r   r@   rA   rD   r   r   r   r   r   r   rK   r   rN   rO   rC   )r'   rP   r^   r   r   rV   r   r   r   set_device_id   4   

"r`   z/device/<device_id>/node_namec              
   C   sJ  zut jdddp	i }|dpd dd }|s#tddd	d
fW S |  dd } | s7tddd	d
fW S t }z+t| | }|d| ||f W d   n1 sWw   Y  |	  W |
  n|
  w td| |ddfW S  tjy   tddd	df Y S  ty } ztdt|d	dfW  Y d}~S d}~ww )zlAssign new node name: body {"node_name": "Living Room"}. Device will pick it up on next POST (max 50 chars).Tr    r(   r,   Nr-   Fznode_name requiredr#   r&   r+   r\   zINSERT INTO device_command (device_id, command, new_node_name) VALUES (%s, 'nosleep', %s) ON DUPLICATE KEY UPDATE new_node_name = %s)r$   r]   r6   r;   r>   r?   r_   )r'   rP   new_namer   r   rV   r   r   r   set_node_name   ra   rc   z/api/readingsc                  C   s   t ttjddd} zGt }z>| %}|d| f | }|D ]}|dr2|d 	 |d< q#W d   n1 s=w   Y  t
d|dW |  W S |  w  tjyf   t
d	d
ddf Y S w )z-Return recent moisture readings (latest 100).limitd   r?   zSELECT id, device_id, node_name, moisture, battery_voltage, command, firmware_version, created_at FROM MoistureLevel ORDER BY id DESC LIMIT %s
created_atNTr$   readingsFr>   r#   )minrE   r   argsrA   r   r   r   fetchall	isoformatr   rK   r   rN   )rd   r   r   rowsrr   r   r   api_readings   s*   

	ro   z/api/readings/latestc                  C   s   zEt  } z<|  #}|d | }|D ]}|dr$|d  |d< qW d   n1 s/w   Y  td|dW |   W S |   w  tj	yX   tdddd	f Y S w )
z-Return the latest reading per device_id only.a	  
                    SELECT m.id, m.device_id, m.node_name, m.moisture, m.battery_voltage, m.command, m.firmware_version, m.created_at
                    FROM MoistureLevel m
                    INNER JOIN (
                        SELECT device_id, MAX(id) AS max_id FROM MoistureLevel GROUP BY device_id
                    ) latest ON m.device_id = latest.device_id AND m.id = latest.max_id
                    WHERE m.created_at >= NOW() - INTERVAL 20 MINUTE
                    ORDER BY m.device_id
                rf   NTrg   Fr>   r#   r?   )
r   r   r   rk   rA   rl   r   rK   r   rN   )r   r   rm   rn   r   r   r   api_readings_latest  s"   

	
rp   z/api/readings/clear-oldDELETEc                  C   s   zKt jjddtd} tdt| d} t }z1| }|d| f |j	}W d   n1 s/w   Y  |
  td|dd	fW |  W S |  w  tjy^   td
dddf Y S w )zLDelete records older than the given days (default 1). Returns count deleted.days   )typeim  zDDELETE FROM MoistureLevel WHERE created_at < NOW() - INTERVAL %s DAYNT)r$   deletedr;   Fr>   r#   r?   )r   rj   rA   rE   maxri   r   r   r   rowcountr   r   rK   r   rN   )rr   r   r   ru   r   r   r   api_readings_clear_old#  s$   
rx   z/api/device/<device_id>/commandGETc                 C   s$  |   dd } | stddddfS zkt }zbt| | ?}|d| f | }|r2|d nd	   }|d
vr>d	}|rI|dpFd  nd}|rV|dpSd  nd}W d   n1 sbw   Y  td||pmd|ppddW |	  W S |	  w  t
jy   tddddf Y S w )z?Return current command for device (for dashboard toggle state).Nr+   FrY   r#   r&   r/   r0   r1   r2   r5   r,   r6   T)r$   r0   r5   r6   r>   r?   )rD   r   r   r   r   r   rI   rJ   rA   rK   r   rN   )r'   r   r   rQ   r0   r5   r6   r   r   r   get_device_command9  s0   
rz   /c                   C   s   t dS )Nzmoisture/dashboard.html)r   r   r   r   r   	dashboardU  s   r|   z/healthc                   C   s   t ddidfS )Nr$   Tr;   )r   r   r   r   r   healthZ  s   r}   ) __doc__rL   r   flaskr   r   r   r   r   configr   r   r	   r
   __name__bpcursors
DictCursorr   r   r   routerW   r[   r`   rc   ro   rp   rx   rz   r|   r}   r   r   r   r   <module>   sD    
	,
T







