python-mobius 0.8.2__py3-none-any.whl → 0.8.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
mobius/__init__.py CHANGED
@@ -82,7 +82,7 @@ from .mob import (
82
82
  MAX_PUMP_PRIMITIVE_SIZE,
83
83
  )
84
84
 
85
- __version__ = "0.8.2"
85
+ __version__ = "0.8.4"
86
86
 
87
87
  __all__ = [
88
88
  "__version__",
mobius/device.py CHANGED
@@ -47,7 +47,7 @@ from .schedule import (
47
47
  )
48
48
  from .scenes import Scene, ActiveScene, decode_configured_scenes, decode_active_scene
49
49
  from .modifiers import (
50
- AcclimationInfo, is_night_segment, lunar_percent_reduction,
50
+ AcclimationInfo, is_night_segment, lunar_percent_reduction, lunar_days_into_phase,
51
51
  )
52
52
  from .power import ChannelPowerInfo, channel_percent_value
53
53
  from .pump_status import PumpFlowRange, BatteryBackupInfo, BoostedBatteryInfo
@@ -163,6 +163,35 @@ class LightPollResult:
163
163
  # failed and decode_schedule_intensity() had truly nothing to fall
164
164
  # back to.
165
165
  schedule_intensity: Optional[float] = None
166
+ # The device's own raw LunarPhasesEnabled (907) setting -- confirmed
167
+ # against the app's own "Lunar" chip, whose on/off state this
168
+ # matches exactly, at any time of day.
169
+ #
170
+ # A second, real, confirmed bug lived here before this field
171
+ # existed, same shape as schedule_intensity's own bug above:
172
+ # intensities.diagnostics["lunar_enabled"] is NOT this. Read
173
+ # process_light_intensities()'s own source: outside the night
174
+ # segment, it unconditionally sets lunar_enabled = None ("not
175
+ # checked/relevant outside the night segment" -- true for ITS OWN
176
+ # calculation, since the toggle only affects anything during the
177
+ # night segment, but not true for a caller that wants to know the
178
+ # device's own current setting regardless of time of day). A
179
+ # caller reading diagnostics["lunar_enabled"] during the day always
180
+ # gets None, even when the device's own toggle is genuinely on --
181
+ # this field is the one that's always correct, any time of day.
182
+ lunar_enabled: Optional[bool] = None
183
+ # The current lunar-cycle phase day (0-29, lunar_days_into_phase()'s
184
+ # own "days since new moon" convention), for a caller that wants to
185
+ # display the current moon phase -- not the same thing as the raw
186
+ # date this is computed from (which is only useful internally, for
187
+ # the schedule's own night-segment reduction calculation, and isn't
188
+ # exposed here at all). Falls back to today's local date when the
189
+ # device itself didn't provide one (Epoch/LocalTime unavailable),
190
+ # matching process_light_intensities()'s own "local_fallback"
191
+ # philosophy, so this is populated whenever `now` itself is valid --
192
+ # which it always is, since callers of get_light_poll_batch() are
193
+ # required to supply it.
194
+ lunar_phase_day: Optional[int] = None
166
195
 
167
196
 
168
197
  @dataclass
@@ -565,6 +594,7 @@ def decode_light_poll_from_batch(
565
594
  )
566
595
  return LightPollResult(
567
596
  schedule_points=points, intensities=intensities, used_batch=True, schedule_intensity=schedule_intensity,
597
+ lunar_enabled=lunar_enabled, lunar_phase_day=lunar_days_into_phase(lunar_date or now.date()),
568
598
  )
569
599
 
570
600
 
@@ -3463,6 +3493,35 @@ class MobiusDevice:
3463
3493
  return False
3464
3494
  return decode_lunar_enabled(raw)
3465
3495
 
3496
+ async def set_lunar_enabled(self, enabled: bool) -> None:
3497
+ """Writes LunarPhasesEnabled (907) -- confirmed byte-for-byte
3498
+ against the app's own compiled bytecode (LunarInfo.smali's own
3499
+ save() method): a single byte, the boolean enabled field stored
3500
+ directly as 0 or 1, via the exact same SetC2AttrFsciRequest.
3501
+ addAttribute() mechanism this library's own set_attribute()
3502
+ uses. This is exactly what the app's own "Lunar" chip (top of
3503
+ the light schedule editor) toggles: clicking it turns
3504
+ lunar-phase night-time dimming on/off, and while enabled that
3505
+ same chip also displays the current moon phase.
3506
+
3507
+ No group/broadcast involved -- confirmed by tracing
3508
+ LightingFragment's own saveLunar(), which loops over every
3509
+ light device in the tank and calls this same per-device,
3510
+ ungrouped write on EACH one individually (not a single
3511
+ mesh-wide broadcast the way set_time_to_now() works). The
3512
+ app's own UI applies the same new value to every light in the
3513
+ tank with that one tap, but the underlying wire mechanism is
3514
+ still one ordinary, targeted write per device -- this method
3515
+ only ever writes to the single device it's called on, matching
3516
+ every other attribute write in this library.
3517
+
3518
+ No which/schedule parameter -- unlike Schedule1Intensity/
3519
+ Schedule2Intensity, there's only ever a single
3520
+ LunarPhasesEnabled attribute (confirmed: no LunarPhasesEnabled2
3521
+ exists), so this applies to the light as a whole rather than to
3522
+ either schedule individually."""
3523
+ await self.set_attribute(C2Attribute.LunarPhasesEnabled, bytes([1 if enabled else 0]))
3524
+
3466
3525
  async def get_acclimation_info(self) -> Optional[AcclimationInfo]:
3467
3526
  """
3468
3527
  Fetches all 4 acclimation attributes (AcclimationEnabled/Period/
@@ -3811,9 +3870,12 @@ class MobiusDevice:
3811
3870
  points = await self.get_light_schedule(which)
3812
3871
  intensities = await self.get_current_light_intensities(which, minute_of_day, now)
3813
3872
  schedule_intensity = await self.get_schedule_intensity(which)
3873
+ lunar_enabled = await self.get_lunar_enabled(which)
3874
+ lunar_date = await self.get_app_lunar_date()
3814
3875
  return LightPollResult(
3815
3876
  schedule_points=points, intensities=intensities, used_batch=False,
3816
- schedule_intensity=schedule_intensity,
3877
+ schedule_intensity=schedule_intensity, lunar_enabled=lunar_enabled,
3878
+ lunar_phase_day=lunar_days_into_phase(lunar_date or now.date()),
3817
3879
  )
3818
3880
 
3819
3881
  async def get_full_poll_batch(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: python-mobius
3
- Version: 0.8.2
3
+ Version: 0.8.4
4
4
  Summary: Reverse-engineered Python client for the Mobius BLE protocol (EcoTech Marine VorTech/Radion, AquaIllumination, Neptune Systems, NYOS)
5
5
  Project-URL: Homepage, https://code.r3pek.org/r3pek/python-mobius
6
6
  Project-URL: Documentation, https://code.r3pek.org/r3pek/python-mobius/src/branch/main/documentation
@@ -1,9 +1,9 @@
1
- mobius/__init__.py,sha256=yON2rPOxmGrhuAQ753NNSXkaUWFuUCc3R5JZTbRkvlY,7620
1
+ mobius/__init__.py,sha256=zJ7qeHhqVm9-cdr0eJP8VTPoXL95gcjpIPkQV8esqhw,7620
2
2
  mobius/cli.py,sha256=DJNKcBB7VCROZ90QmFWeCkRzPCRfoTaeTZaCLJ41s-Y,79735
3
3
  mobius/coap.py,sha256=ZRBy8ivgl1o2cVLjIp-df8Jg34c58K0_1Oec1P-gQI4,10464
4
4
  mobius/constants.py,sha256=OlbC0GSPT3Lp7rGfIqSMkAqY535oNNRRoOd15TTVt7k,46286
5
5
  mobius/crc.py,sha256=T6PWTp3gAHu-C05ua2LH8z8leD8mSjizM7nuC9G8KhU,2643
6
- mobius/device.py,sha256=Ti6jBr_gXDXXM2SlqLYV471p1tIhdDk4NTiBTWNXxPY,201501
6
+ mobius/device.py,sha256=H-q8SDEjbS7OeapUJTOY4dUFCdc88j2NRdE-gQf8Ib8,205390
7
7
  mobius/device_status.py,sha256=tKZwi12fS3PVl7F-Pr5tvTNOTAttaOtAWom7ahl0LZM,33080
8
8
  mobius/discovery.py,sha256=-N_RK8ZK91Ep-XePm9cHJa44yKiBL_Ind9wFHi_TZ-8,11742
9
9
  mobius/dump.py,sha256=2uy-bq8Ja2W3D4dz9VboSO_KqYQ4bE8xmVxYTxWOjXg,16525
@@ -17,8 +17,8 @@ mobius/pump_status.py,sha256=qLprWM0gAoltZj7w8ykdXHPfBDMl-QnBOwrmBlvknIA,1536
17
17
  mobius/relay.py,sha256=BmUc1l0mDOt9isTGs7wCh0NxehbMxfaTzm3cYs7VucA,26445
18
18
  mobius/scenes.py,sha256=Wt0Ys2x1YjF8MNDEd6kxWqYBIYPdBSbfzXD-L5ZKzag,3592
19
19
  mobius/schedule.py,sha256=BDm8Uh8O85AlAZuR7i7pgqKC2GNXcV_HikFoG6_3Xgo,17590
20
- python_mobius-0.8.2.dist-info/METADATA,sha256=w2YtMRx_vpnctt1CYM-VEw-uDomBvXawtGpgl_BFC6Q,9899
21
- python_mobius-0.8.2.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
22
- python_mobius-0.8.2.dist-info/entry_points.txt,sha256=Q2hlcek-bWm70zvd12v32essflPgC2su-dKDHAqbPoE,48
23
- python_mobius-0.8.2.dist-info/licenses/LICENSE,sha256=7a72Msu2Q-TnoiFxemxEGkwafJGObk1W3rw9hzmyM_Y,17984
24
- python_mobius-0.8.2.dist-info/RECORD,,
20
+ python_mobius-0.8.4.dist-info/METADATA,sha256=zBB4TSObJ1G1aMtSgw4J69til5vj1wevuz1suQ99beY,9899
21
+ python_mobius-0.8.4.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
22
+ python_mobius-0.8.4.dist-info/entry_points.txt,sha256=Q2hlcek-bWm70zvd12v32essflPgC2su-dKDHAqbPoE,48
23
+ python_mobius-0.8.4.dist-info/licenses/LICENSE,sha256=7a72Msu2Q-TnoiFxemxEGkwafJGObk1W3rw9hzmyM_Y,17984
24
+ python_mobius-0.8.4.dist-info/RECORD,,